c++templatesdesign-patternsstdbindstdany

Is it possible to forward function calls with matching template type for a std::any-like container?


I haven't found a way to achieve what I want but I'm not knowledgeable enough to know if its impossible. Help would be appreciated.

The main data data container in our software behaves a bit like a std::variant or std::any: It has a base class BaseContainer that provides a type enum. The derived instance DataContainer holds the actual data in a typed tensor member variable. So a simplified example boils down to something like this:

BaseContainer* vContainer = new DataContainer<float>({1000000});
if (vContainer->getType() == DataTypes::FLOAT)
    const Tensor<float>& vTensor = dynamic_cast<DataContainer<float>>(vContainer)->getData();

We have many methods that process data based on the underlying templated type and dimensions:

template<typename T>
void processData(const tensor<T>& aTensor, ...other arguments...);

The problem is, for every method like processData() that we want to call with a BaseContainer, we need to write a binding method that unravels the possible types to call the typed version of processData():

void processData(BaseContainer* aContainer) {
    switch (vContainer->getType()) {
        case DataTypes::INT8:
            return processData(dynamic_cast<DataContainer<int8_t>>(vContainer)->getData());
        case DataTypes::UINT8:
            return processData(dynamic_cast<DataContainer<uint8_t>>(vContainer)->getData());
        case DataTypes::INT16:
            return processData(dynamic_cast<DataContainer<int16_t>>(vContainer)->getData());
        case DataTypes::UINT16:
            return processData(dynamic_cast<DataContainer<uint16_t>>(vContainer)->getData());
...
        default:
            throw(std::runtime_error("Type not supported"));
    }
}

My question is: Is it possible to make a single "adapter" method (in any released version of c++) that can take a function (like processData()), a BaseContainer and potentially a list of arguments, and invoke the correct template binding of this function with the arguments?

I failed to bind a template function dynamically because I was not able to pass the name without the template type. Yet the template type would need to be dynamic based on the BaseContainer. But maybe there are other means to achieve what I want to do? I'm very curious about any solution, mostly also to extend my understanding, as long as the complexity of the solution is below writing hundreds of adapter methods.

If nothing else, would it be possible to generate the "adapter" methods using preprocessor macros?


Solution

  • You cannot pass overloads by name, but you can pass functor with overloaded operator() as generic lambda have.

    So

    template <typename F>
    auto dispatch(BaseContainer& vContainer, F f) {
        switch (vContainer.getType()) {
            case DataTypes::INT8:
                return f(dynamic_cast<DataContainer<int8_t>&>(vContainer).getData());
            case DataTypes::UINT8:
                return f(dynamic_cast<DataContainer<uint8_t>&>(vContainer).getData());
            case DataTypes::INT16:
                return f(dynamic_cast<DataContainer<int16_t>&>(vContainer).getData());
            case DataTypes::UINT16:
                return f(dynamic_cast<DataContainer<uint16_t>&>(vContainer).getData());
    ...
            default:
                throw (std::runtime_error("Type not supported"));
        }
    }
    
    

    with usage

    dispatch(vContainer, [](auto* data){ return processData(data); });