C++ - создать корзину для любой лямбды

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

enum class Format
{
    FORMAT_1,
    FORMAT_2
    //, ...
};

template<Format F>
struct SomeData
{
    //...
};

template < template<Format> typename Processing >
struct ProcessingForAllFormats
{
    Processing<Format::FORMAT_1> processingObject1;
    Processing<Format::FORMAT_2> processingObject2;
    //...
};

template < template<Format> typename Processing >
decltype(auto) makeProcessingForAllFormats(Processing lambda)
{
    //I know this function is completely wrong as
    //concrete instantation needs concrete type as an argument
    //instead of a template
    return ProcessingForAllFormats<Processing>();
}

int main() {
    auto processing = makeProcessingForAllFormats(
        [](SomeData<auto> data) {/*...*/}
        );
    return 0;
}

Это упрощенное изображение моей проблемы. Одним словом - я хочу хранить экземпляры обработок для любых объектов SomeData для будущего использования. Я не знаю, возможно ли это в C++14 или даже в C++17. И я знаю, что это было бы легко, если бы я использовал динамический полиморфизм вместо статического, но производительность в этом случае для меня много значит.

РЕДАКТИРОВАТЬ:

Как заметил TartanLlama - использование функторов действительно проще в реализации, но намного сложнее в использовании. Я лечу Format, SomeData а также ProcessingForAllFormats например, если это было частью библиотеки /API, и я хочу дать пользователю этой "библиотеки" как можно больше простоты использования. Лямбды предназначены для этого. Предложение @AndyG полезно - для лямбда-ов реализация ProcessingForAllFormats должна быть разной. Но я понятия не имею, достаточно ли мощны лямбда-шаблоны в C++14/17 для создания такого API.

2 ответа

Решение

Как насчет упаковки универсальной лямбды с интерфейсом, ограниченным вашими желаемыми типами:

enum class Format
{
    FORMAT_1,
    FORMAT_2
    //, ...
};

template<Format F>
struct SomeData
{
    //...
};

template <typename GenericProcessing, Format format>
struct Restrictor
{
    Restrictor(GenericProcessing genericProcessingObject)
        : genericProcessingObject(genericProcessingObject)
    {}

    decltype(auto) operator()(SomeData<format> data)
    {
        return genericProcessingObject(data);
    }

private:
    GenericProcessing genericProcessingObject;    
};

template <typename GenericProcessing>
struct ProcessingForAllFormats
{
    Restrictor<GenericProcessing, Format::FORMAT_1> processingObject1;
    Restrictor<GenericProcessing, Format::FORMAT_2> processingObject2;
    //...

    ProcessingForAllFormats(GenericProcessing genericProcessingObject)
        : processingObject1(genericProcessingObject)
        , processingObject2(genericProcessingObject)
        //...
    {}
};

template <typename GenericProcessing>
decltype(auto) makeProcessingForAllFormats(GenericProcessing genericProcessingObject)
{
    return ProcessingForAllFormats<GenericProcessing>(genericProcessingObject);
}

int main() {
    auto processing = makeProcessingForAllFormats(
        [](auto data) {/*...*/});
    processing.processingObject1(SomeData<Format::FORMAT_1>{}); // ok
    //processing.processingObject1(SomeData<Format::FORMAT_2>{}); // fail by design, expects SomeData<Format::FORMAT_1>
    //processing.processingObject2(SomeData<Format::FORMAT_1>{}); // fail by design, expects SomeData<Format::FORMAT_2>
    processing.processingObject2(SomeData<Format::FORMAT_2>{}); // ok
}

Делать это с лямбдой звучит как большая боль, так как это не закрытие, которое генерируется из шаблона, это operator(), Это намного проще с шаблоном функтора:

enum class Format
{
    FORMAT_1,
    FORMAT_2
    //, ...
};

template<Format F>
struct SomeData
{
    //...
};

template < template<Format> typename Processing >
struct ProcessingForAllFormats
{
    Processing<Format::FORMAT_1> processingObject1;
    Processing<Format::FORMAT_2> processingObject2;
    //...
};

template <Format F>
struct Processor {
    void operator() (SomeData<F> data) { /*...*/ } 
};

int main() {
    auto processing = ProcessingForAllFormats<Processor>{};
}
Другие вопросы по тегам