Search code examples
c++c++11templatesvariadic-templatestemplate-meta-programming

How to migrate array-initializer of arguments to variardic template arguments in C++11


I have some code that was written before we had c++11 compilers across our platforms. Finally we can start using c++11 features as the last platform has finally caught up. As a result, I am trying to cleanup some of the long "function with N arguments via copy-pasting" chains with variardic templates. However, I will admit that I don't quite know how to solve the following case. I have a Format() method that packs arguments into variants and then passes them into a function buried in a source file (to reduce bloat, include dependencies, etc). How do I unpack the template arguments into an array to pass into the method?

struct Variant
{
    enum ValueType { Integer, Float };
    Variant(int _value) { value._asInt = _value; valueType = Integer}
    // ... and other constructors

    union Value
    {
        int _asInt;
        // ... other types including specific user types
    } value;
    ValueType valueType;
};

// Implementation burried in source file
extern size_t FormatStringImpl(const char* format, char * dest, size_t capacity, Variant variants[], size_t numVariants);

template<class T0>
size_t Format(const char* format, char* dest, size_t capacity, T0 var0)
{
    Variant variants[] = { Variant(var0) };
    return FormatStringImpl(format, dest, capacity, variants, sizeof(variants) / sizeof(variants[0]));
}

template<class T0, class T1>
size_t Format(const char* format, char* dest, size_t capacity, T0 var0, T1 var1)
{
    Variant variants[] = { Variant(var0), Variant(var1) };
    return FormatStringImpl(format, dest, capacity, variants, sizeof(variants) / sizeof(variants[0]));
}

// and on and on

void foo()
{
    const size_t length = 1024;
    char output[length];
    Format("Values are {0}, {1}, {2}", output, length, 10, 20, 30);
}

The details are somewhat more complex, but the gist of it is a way to capture the type/data and pull it into the source file for processing. I would like to perform all allocations on the stack so using a std::vector in an unpack loop here won't work.


Solution

  • Format can be simplified to:

    template<class ... Ts>
    size_t Format(const char* format, char* dest, size_t capacity, Ts... vars)
    {
        Variant variants[] = { Variant(vars)... };
        return FormatStringImpl(format, dest, capacity, variants, sizeof...(vars));
    }
    

    I also replaced sizeof(variants) / sizeof(variants[0]) by sizeof...(vars)