C++ вернуть из функции int разного размера

185
31 августа 2018, 17:40

Нужна функция, которая будет способна возвращать как int32, так и int64. Как это наиболее грамотно реализовать?

Цель в том, чтобы вынести в функцию код, получающий из бинарного файла целые числа указанного размера по указанному эндиану.

Answer 1

Вариант 1. Предлагаю вам вариант с template

 template<typename T> T getNumData(void* buff, int offs){
    T ret;
    for (int i=0;i<sizeof(T);i++)  ((char*)&ret)[i] =  ((char*)buff)[i+offs];
    return ret;
      }

С обратным индианом аналогично но ((char*)&ret)[sizeof(T)-i-1] Возможно этот вариант кривеникий, как правильно с кастами писать - не знаю.

При использовании придётся указывать желаемый тип getNumData<int32>(q,0) это вам позволит сразу декодировать все 3-4 варианта от 8бит до 64.

Если ф-цию getNumData переделать на такую template<typename T> void getNumData(T& ret, void* data, int offs) тогда не придётся указывать <int32> - можно будет записать getNumData(retvar,q,0); Вариант достаточно хороший по быстродействию.

Еще есть варианты:

  • Вернуть union (просто, но будет а) потеря знака б) невозможно для обратного indian.

  • Вернуть int64 и кастить (невозможно для обратного indian)

  • Вернуть класс, и прописать правила каста к каждому из типов (больше кода, удобно, но можно запутаться) - implicit cast.

Вариант 2. От лишних <> можно избавится "жертвуя" дополнительным классом (некоторые компиляторы съедят struct, но всётаки операторы относятся к классам class). Покажу implicit cast - так это называется, т.е. компилятор смотрит к какому классу идёт каст, и делает вызов определенного оператора. Нужно этот метод проверять дизассемблером, что б компилятор вызовы new и delete не лепил. Для одной переменной компилятор не создаёт конструкторы деструкторы. При использовании могут быть потери скорости, поэтому лучше проверить. В упрощённой схеме получится так:

class /*struct*/ autocast {              
    public:
    void *data; 
     // кастим к int32, в комментарии вариант для обратного indian
    operator int32() {  return  *(int32*)data; /*return getData<int>(data,0)*/ } 
    // кастим к int64
    operator int64() {  return  *(int64*)data; }
    };
//И можно запилить 
    autocast /*inline*/ getNumDataVar(void* data){ /*Дописать offs*/
        autocast ret;
        ret.data = data;
        return ret;
      }
// вызов делается так (но тип слева должен быть одним из тех,
// для которого существует оператор implicit cast) 
int32 query =  getNumDataVar(q);

Недостаток - вместо одного вызова - будет два вызова (некоторые компиляторы могут сделать 4-ре, но указанием inline может быть скомпилирован как один вызов так и ноль, зависит от компилятора), зато удобнее в использовании. Недостаток - больше кода - для каждого типа прийдётся писать отдельный каст, в отличии от первого варианта, но иногда такое разделение позволяет добавить быстродействие (легче избавится от циклов и т п).

Answer 2

В C++ нельзя перегружать функции по возвращаемому значению. Иными словами следующая перегрузка неправльна

int32 getInt();
int64 getInt();

Но есть другие способы:

  1. Сделать две разные функции и вызывать необходимую:

    int32 getInt32();
    int64 getInt64();

    Можно сделать эту функцию шаблонной но это особо ничего не изменит.

  2. Не возвращать значение вообще. Передавать указатель или ссылки на обеъкты:

    void getInt(int32&, int64&);
  3. Возвращать структуру из пары элементов

    struct UniversalInt {
       int32 i32;
       int64 i64;
    };
    UniversalInt getInt();

    Но тут вознкают проблемы: во-первых не зависимо от резульата память будет выделена под оба элемента, а использован один, а во-вторых неизвестно какой тип именно был возвращен.

  4. Использовать union. Это частично решит проблему с выделением лишней памяти. Этот вариант не стоит использовать в такой реализации вообще.

     union UniversalInt {
       int32 i32;
       int64 i64;
     };
     UniversalInt getInt();
  5. К union добавить информацию о возвращенном типе. Хорошая новость заключатся в том, что такая структура уже есть - std::variant; плохая новость заключается в том, что добавлен std::variant был только в С++17.

     std::variant<int32, int64> getInt();

    Как вариант можно использовать boost::variant. Для POD типов подобная структура будет довольно просто выглядеть и её можно написать самостоятельно. Как только оказывается, что подходит variant имеет смысл задуматься об использовании шаблона посетитель.

На мой взгляд лучшим вариантом будет первый.

READ ALSO
Поворачивание Perspective Camera

Поворачивание Perspective Camera

Как поворачивать Perspective Camera, когда по экрану проводят пальцем (как в Майнкрафте Bedrock Edition)?

151
Vaadin 8. Компонент Upload. Нужно ли закрывать OutputStream?

Vaadin 8. Компонент Upload. Нужно ли закрывать OutputStream?

Код взят для примера с офф сайта ваадина:

159