Войти
ПрограммированиеФорумОбщее

C++ пройтись по листу типов и создавать объекты до определенного момента

#0
(Правка: 14:56) 14:54, 13 авг. 2019

Всегда не дружил с шаблонами и мета-программированием с++.
Есть подобная иерархия:

Абстрактный класс и куча разных наследников в одном поколении.

Я хочу в определенном порядке их создавать и пробовать инициализировать, а именно до первого успешно созданного и инициализированного.

Порылся я в интернете, нашел реализации списка типов, например: https://blog.galowicz.de/2016/05/08/compile_time_type_lists/
но как им воспользоваться я не понимаю.

Вот у меня список:

using MyList = make_t<Type1, Type2, Type3>;

так же есть функция, которая создает и инициализирует объект требуемого типа:

template <typename DecoderType>
inline VideoDecoderPtr tryCreateDecoder(DemuxerPtr demuxerPtr, uint8_t videoTrackNumber)
{
    auto provider = std::unique_ptr<VideoSampleProvider>(new VideoSampleProvider(demuxerPtr,
                                                                                 videoTrackNumber));
    auto decoder = VideoDecoderPtr(new DecoderType);
    if( (provider == nullptr) || (decoder == nullptr) )
        return VideoDecoderPtr(nullptr);

    auto initResult = decoder->init(std::move(provider));
    if(initResult != DecoderError::OK)
        return VideoDecoderPtr(nullptr);

    return decoder;
}

сейчас написал такое использование это функции:

...
auto decoder = tryCreateDecoder<NVDEC_VideoDecoder>(demuxerPtr, videoTrackNumber);
    if(decoder != nullptr)
        return decoder;

    decoder = tryCreateDecoder<FFMPEG_VideoDecoder>(demuxerPtr, videoTrackNumber);
    if(decoder != nullptr)
        return decoder;

    return VideoDecoderPtr(nullptr);
...

а хочу заюзать список.
Понимаю, что нужны c++ variadic templates, что нужны две функции обертки, но не понимаю как это все заюзать, чтобы добиться требуемого эффекта. Мне же не все надо создавать объекты.


#1
(Правка: 15:45) 15:44, 13 авг. 2019

MAMOHT-92
> а хочу заюзать список.

Ты уже сам почти всё сделал.
Обрати внимание на то, что у тебя шаблонный параметр DecoderType никак не используется в сигнатуре функции.
Т.е. он порождает тела функций с абсолютно одинаковой сигнатурой.
Т.е. ты можешь просто сделать список из этих функций и напихать в него указатели на функции типа &tryCreateDecoder<NVDEC_VideoDecoder> и др. - они все будут одинакового типа VideoDecoderPtr (*)(DemuxerPtr demuxerPtr, uint8_t videoTrackNumber).

#2
15:48, 13 авг. 2019

P.S.
А если это будет не список, а карта, то получится простенькая фабрика.

#3
(Правка: 16:19) 16:12, 13 авг. 2019

=A=L=X=
> А если это будет не список, а карта, то получится простенькая фабрика.
ну я как бы делаю свою фабрику, которая скрывает  всю процедуру создания итогового декодера.

=A=L=X=
> Т.е. ты можешь просто сделать список из этих функций и напихать в него
> указатели на функции типа &tryCreateDecoder<NVDEC_VideoDecoder>
ИМХО попахивает извратом каким-то для простой задачи, а может и нет.

#4
16:47, 13 авг. 2019

MAMOHT-92
> ИМХО попахивает извратом каким-то для простой задачи

Наоборот это самый простой способ её решения - есть некий набор генераторов объектов, причём генераторы имеют одинаковый совместимый тип и сигнатуру. Поэтому их легко помещать в любые контейнеры и вызывать как хочется.
Самые простые генераторы - функции (соответственно с одинаковой сигнатурой).
Когда понадобятся какие то более сложные вещи - генератор может стать объектом (опять таки наследником от какого общего класса-генератора).
А так штука в которую они все помещаются и есть фабрика.
Потом еще желательно решить вопрос как можно более простой и естественной регистрации генераторов в фабрике, но это уже всё более и более наворачивающиеся усложнения.
А статические приёмы метапрограммирования тут сбоку-припёку.

#5
(Правка: 18:15) 17:45, 13 авг. 2019

типа:

auto spawn_instance_by_id=[](auto id)->i_base*{
  if(type2id<type0>::value==id)return spawn<type0>();
  if(type2id<type1>::value==id)return spawn<type1>();
  if(type2id<type2>::value==id)return spawn<type2>();
  if(type2id<type3>::value==id)return spawn<type3>();
  return nullptr;
};
что-то такое?


upd:

i_base*get_first_ok_spawned_elem_for_i_base(t_args&args){
  #define F(TYPE){if(auto*ptr=type2factory<TYPE>::spawn(args))return ptr;}
  F(type0);F(type1);F(type2);F(type3);
  #undef F
  return nullptr;
}
или так?
+ Показать

#6
18:15, 13 авг. 2019

Adler
> auto spawn_instance_by_id=[](auto id)->i_base*{
> if(type2id<type0>::value==id)return spawn<type0>();
> if(type2id<type1>::value==id)return spawn<type1>();
> if(type2id<type2>::value==id)return spawn<type2>();
> if(type2id<type3>::value==id)return spawn<type3>(); return nullptr; };

хотел не писать такое.

А что во втром происходит не распарсил.

#7
(Правка: 19:58) 19:58, 13 авг. 2019

А обычный рекурсивный обход вариадика чем не подходит?
И будет просто 2 точки выхода: первая успешная генерация и дно рекурсии (нет успешной генерации)

#8
20:21, 13 авг. 2019

MAMOHT-92
Если я правильно понял, то тебе можно использовать std::tuple и итерироваться по всем его элементам. К сожалению, итерация по элементам tuple не тривиальна (хотя в новых плюсах вроде что-то обещали). Но если у тебя msvc, то там есть метод:

std::tuple<MyType1, MyType2, MyType3, etc.> my_tuple;

std::_For_each_tuple_element(my_tuple, [](const auto& tuple_element)
{
    // decltype(tuple_element) and other stuff
});

В других компилятор это тоже реализуемо, но нужно чуть больше кода.

#9
20:26, 13 авг. 2019

Но вообще да, тут и обычный обход variadic'а подойдёт, ну а на самом деле лучше вообще вручную с каким-нибудь helper методом типа try_init_as<MyType>().

#10
20:48, 13 авг. 2019

pahaa
> А обычный рекурсивный обход вариадика чем не подходит?
> И будет просто 2 точки выхода: первая успешная генерация и дно рекурсии (нет
> успешной генерации)
что-то не получилось, ибо сигнатура функции одинаковая.

#11
3:18, 14 авг. 2019

Не знаю, чем там сигнатура мешает, вроде всё работает

template<class... Args>
class Generator {
  
  template<class T>
  Decoder* createDecoder(Ptr ptr, int track) {
    return tryCreateDecoder<T>(ptr, track);
  }

  template<class First, class Next, class... Other>
  Decoder* createDecoder(Ptr ptr, int track) {
    Decoder* result = createDecoder<First>(ptr, track);
    if (result) {
      return result;
    }
    return createDecoder<Next, Other...>(ptr, track);
  }

public:
  Decoder* createDecoder(Ptr ptr, int track) {
    return createDecoder<Args...>(ptr, track);
  }

};
ПрограммированиеФорумОбщее