Можно ли заставить функцию возвращать имя типа в C ++? - PullRequest
0 голосов
/ 01 ноября 2019

Мой вопрос касается возможности создания функции C ++ или аналогичной конструкции, которая возвращает имя типа (в отличие от объекта / ссылки / указателя некоторого типа, как это обычно бывает).

Как показано в приведенном ниже коде, у меня есть общий класс «Image», который просто содержит указатель на данные (void *), размер данных и переменную-член, определяющую способ представления данных. Мне нужна гибкость для представления 8-битных, 16-битных, ... типов изображений с одним и тем же классом.

Когда я хочу манипулировать данными изображения, я в настоящее время должен использовать конструкцию switch / case (какпоказанная в примере функция CopyImage()) позволяет повторно интерпретировать указатель данных void * на соответствующий тип перед выполнением копирования. К сожалению, эта та же самая парадигма переключателя / случая повторяется повсюду, когда я расширяюсь функциями, которые делают что-то, кроме CopyImage (). Это кажется грязным и дублирующим и ограничивает мою способность добавлять поддержку для большего количества типов без репликации дополнительных случаев в тоннах других функций.

enum class datatype_t {
    u8,
    u16
};

class Image {       // can be an image with 8 or 16-bit pixel representation
public:
    void * dataPtr_;
    datatype_t dataType_;
    size_t pixels_;
};

void CopyImage(Image& source, uint16_t * dst_ptr) {     // function to copy image agnostic to data represenation
    switch (source.dataType_) {
    case datatype_t::u8:
        std::copy_n(reinterpret_cast<uint8_t*>(source.dataPtr_), source.pixels_, dst_ptr);
        break;
    case datatype_t::u16:
        std::copy_n(reinterpret_cast<uint16_t*>(source.dataPtr_), source.pixels_, dst_ptr);
        break;
    }
}

int main()
{
    const size_t image_size = 100;

    std::vector<Image> image_library;

    // create Image objects, populate details, push to image library 
    Image source_image8;
    source_image8.dataType_ = datatype_t::u8;
    source_image8.pixels_ = image_size;
    source_image8.dataPtr_ = new uint8_t[image_size];
    image_library.push_back(std::move(source_image8));

    Image source_image16;
    source_image16.dataType_ = datatype_t::u16;
    source_image16.pixels_ = image_size;
    source_image16.dataPtr_ = new uint16_t[image_size];
    image_library.push_back(std::move(source_image16));

    auto destination = new uint16_t[image_size];
    CopyImage(image_library[0], destination);       // copy a 8-bit image into 16-bit destination
    CopyImage(image_library[1], destination);       // copy a 16-bit image into 16-bit destination

    // deletes, etc to follow (not shown for conciseness)

}

То, что я хочу - это функция, которую я могу включить в <...> из reinterpret_cast, которая при вызове возвращает имя типа (то есть uint8_t, uint16_t), и т.д). Эта функция должна быть функцией-членом класса Image, ссылаться на переменную dataType_ и предоставлять имя типа - аналогично тому, что оператор switch / case делает явно для каждого возможного типа - но ее нужно поддерживать только в одном месте, так как онафункция, которая будет вызываться из любого места, где я пытаюсь выполнить операцию reinterpret_cast <>.

Например, функция с именем Image::ReturnType(), которая может использоваться следующим образом:

std::copy_n(reinterpret_cast<source.ReturnType()>(source.dataPtr_), source.pixels_, dst_ptr);

и быть определена примерно так:

typename Image::ReturnType() {
   switch (dataType_) {
   case u8:
      return uint8_t;
   case u16:
      return uint16_t;
   }
}

Я понимаю этоВопрос заключается в том, чтобы попросить найти решение, которое использует преимущества полиморфизма или связано с шаблонизацией класса Image, так что у меня есть отдельные типы Image<uint8_t> и Image<uint16_t>, но это не позволяет мне хранить кучу объектов Image (с различными представлениями пикселей). ) в одном std::vector<Image>.

Примечание. Если есть способ сохранить в одном std::vector объекты с различными типами специализаций шаблонов, я также открыт для этого решения, но ябоюсь, что не существует.

Большое спасибо!

Ответы [ 3 ]

2 голосов
/ 01 ноября 2019

Нет.

Функция может возвращать только объект или ссылку (или void).

Что вы можете сделать, это сопоставить значение с типом, используя псевдоним шаблонного типа, илипсевдоним типа члена шаблона класса. Пример:

template<datatype_t>
struct image_return {};

template<>
image_return<u8> {
    using type = std::uint8_t;
};

template<>
image_return<u16> {
    using type = std::uint16_t;
};

Однако для вашего случая использования вместо void* может быть лучше использовать std::variant<std::uint8_t*, std::uint16_t*>. Или, что еще лучше, используйте вариант std::vector s, а не голые указатели для выделенной памяти.

2 голосов
/ 01 ноября 2019

Все типы должны быть известны во время компиляции. Тип - это абстракция времени компиляции. Во время выполнения типы более или менее существуют.

Известна ли ваша переменная dataType_ во время компиляции? Если да, конечно, у вас может быть метафункция, возвращающая тип:

using TypeMap = std::tuple<uint8_t, uint16_t>;

struct Image {
    // ...

    template<datatype_t datatype_id>
    using ReturnType = std::tuple_element_t<static_cast<std::size_t>(datatype_id), TypeMap>;
};

Тогда вы можете использовать метафункцию:

// the_type_to_use is uint8_t
using the_type_to_use = Image::ReturnType<datatype_t::u8>;

С другой стороны, если значениеизвестен только во время выполнения, тогда вы должны использовать distpatch времени выполнения. Будь то вариант или виртуальная таблица.

1 голос
/ 01 ноября 2019

Полиморфизм работает с указателями. Вам нужен общий базовый класс без шаблонов для вашего Image<...>. Вы храните (умные) указатели на это в своем контейнере.

class ImageBase {
   ...;
};

template <typename DataType> 
class Image: public ImageBase {
  std::vector<DataType> data_;
    ...;
};

std::vector<std::unique_ptr<ImageBase>> image_library;
...