Wagner Volanin
Wagner Volanin

Reputation: 97

How to create a list of class types, in order to repeatedly call a template function by iterating through it?

I am working with protobufs in C++. Currently, I have a function that receives the protobuf name and the serialized protobuf data. Based on the name, it calls a decode function template to convert the data to json. Sample code:

template <typename T>
static bool DecodeToJson( const std::string& protostring )
{
    T protobuf;
    if( protobuf.ParseFromString( protostring ) == false )
        return false;


    std::string jsonstring;
    google::protobuf::util::MessageToJsonString( protobuf, &jsonstring );
    std::cout << jsonstring;


    return true;
}



static bool ProtoDecode( const std::string& prototype, const std::string& protostring )
{
    if( prototype == "ethconfig" );
        return DecodeToJson<maxim::EthConfig>( protostring );

    if( prototype == "wificonfig" );
        return DecodeToJson<maxim::WifiConfig>( protostring );

    if( prototype == "bluetoothconfig" );
        return DecodeToJson<maxim::BluetoothConfig>( protostring );


    return false;
}

Whevener a new protobuf message is added, I have to manually add an 'if clause' to ProtoDecode( ), just to call DecodeToJson( ) with the correct template type. Although it's an easy edit in this sample code, the real code has many functions just like this, for different kinds of operations.

So what I wanted to do was to add the protobuf name and type to a global list, and change ProtoDecode( ) and all other similar functions to something like:

static bool ProtoDecode( const std::string& prototype, const std::string& protostring )
{
    for( const auto& proto : protoList )
        if( prototype == proto.name );
            return DecodeToJson<proto.type>( protostring );


    return false;
}

This way, it would be much easier to "register" a new proto message. I know the above is incorrect code, but is there any way to achieve something similar?

Upvotes: 0

Views: 207

Answers (1)

Jonathan S.
Jonathan S.

Reputation: 1854

Since template arguments have to be known at compile time, a for loop like this is of course not possible, but template metaprogramming can achieve something similar with recursive templates.

#include <string>
#include <typeinfo>

template<typename T>
bool decodeImpl(const std::string& theData);

template<typename First, typename... Rest>
bool decodeRecursive(const std::string& typeName, const std::string& theData) {
    if (typeName == typeid(First).name()) {
        return decodeImpl<First>(theData);
    } else {
        return decodeRecursive<Rest...>(typeName, theData);
    }
}

template<>
bool decodeRecursive<void>(const std::string& typeName, const std::string& theData) {
    return false;
}

template<typename... T>
bool decode(const std::string& typeName, const std::string& theData) {
    return decodeRecursive<T..., void>(typeName, theData);
}

bool hmm(const std::string& typeName, const std::string& theData) {
    return decode<bool, int, float, double>(typeName, theData);
}

Upvotes: 3

Related Questions