Search code examples
c++templatesc++14instantiationstatic-polymorphism

C++ - create a bucket of instantiations for any lambda


I want to create several instantiations of templated-lambda in one place. The number of instantiations and parameters of each are known at compilation time so I assume that this could be possible. This is generic illustration of what I want to do:

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;
}

This is simplified image of my problem. In one word - I want to store instantiations of processings for any SomeData objects for future use. I don't know if it is possible in C++14 or even C++17. And I know this would be easy if I use dynamic polymorphism instead of static one but performance means a lot for me in this case.

EDIT:

As TartanLlama noticed - using functors is indeed simpler to implement but much harder to use. I treat Format, SomeData and ProcessingForAllFormats like if it was a part of library/API and I want give user of that "liblary" as much ease-of-use as I can. Lambdas are intended to provide that. @AndyG's sugestion is useful - for lambdas ProcessingForAllFormats implementation must be diferent. But I don't have idea if lambda-templates in C++14/17 are powerful enough to build such API.


Solution

  • How about wrapping generic lambda with interface restricted to your desired types:

    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
    }