Oleg Efremov
Oleg Efremov

Reputation: 33

How to specialize a template function by static array of structures

I am a bit in stuck and need a help from C++ template guru. There is a template struct:

template<typename T, typename ID>
struct TypeMapping
{
    T Type;
    char* Name;
    ID Id;
};

and a few template functions like this:

template<typename T, typename ID>
bool TryGetTypeByNameImp(const TypeMapping<T, ID> map[], size_t mapSize,
    const char* name, T& type)
{
    for (size_t i = 0; i < mapSize; i++)
    {
        if (strcmp(map[i].Name, name) == 0)
        {
            type = map[i].Type;
            return true;
        }
    }

    return false;
}

Map (the first parameter) is defined as (there are a few similar maps)

namespace audio
{ 
    const TypeMapping<Type, AMF_CODEC_ID> Map[] = 
    {
        {AAC, "aac", AMF_CODEC_AAC},
        {MP3, "mp3", AMF_CODEC_MP3},
        {PCM, "pcm", AMF_CODEC_PCM_MULAW}
    };

    const size_t MapSize =  sizeof(Map)/sizeof(Map[0]);
}

Map is passed to a function as an argument and I am looking for how to pass it as template parameter so I can use functions like in this sample:

 audio::Type type;
 bool r = TryGetTypeByNameImp<audio::Map>("aac", type);

The only solution I found it is to define a struct which holds static Map and MapSize and use the struct as template parameter but I do not like this solution and I am looking for another one. Does anybody know how to do this?

Upvotes: 3

Views: 593

Answers (2)

Konrad Rudolph
Konrad Rudolph

Reputation: 545588

bool r = TryGetTypeByNameImp<audio::Map>("aac", type);

This is trying to use audio::Map as a type – but it isn’t, it’s a variable. Just pass it to the function as a normal argument:

bool r = TryGetTypeByNameImp(audio::Map, "aac", type);

That said, I have three remarks about your code:

  • Be aware that declaring a function argument as an array (x[]) does in reality declare it as a pointer. Your code uses this correctly, but using the array syntax is misleading. Use a pointer instead.
  • This code is slightly too C-heavy for my taste. While I agree that using raw C-strings is appropriate here, your usage of char* is illegal in C++11, and deprecated in C++03 (since you are pointing to string literals). Use char const*. Furthermore, I’d suggest using a std::string argument in the function, and using the comparison operator == instead of strcmp.
  • You are using an out-parameter, type. I abhor this technique. If you want to return a value, use the return type. Since you also return a success value, use a pair as the return type, unless there’s a very compelling reason not to:

    template<typename T, typename ID>
    std::pair<bool, T> TryGetTypeByNameImp(
        const TypeMapping<T, ID> map[], size_t mapSize,
        const char* name)
    {
        for (size_t i = 0; i < mapSize; i++)
            if (strcmp(map[i].Name, name) == 0)
                return std::make_pair(true, map[i].Type);
    
        return std::make_pair(false, T());
    }
    

Ah, and I’d also consider using a std::vector or std::array here instead of a C array. Then you don’t need to manually shlep the array size around through all the functions which use the array.

Upvotes: 2

ecatmur
ecatmur

Reputation: 157354

You can certainly use the array itself (well, a pointer to it) as a template parameter:

#include <iostream>
template<typename T> struct S { T t; };
S<int> s[] = { { 21 }, { 22 } };
template<typename T, size_t n, S<T> (*m)[n]> void f() { std::cout << (*m)[n - 1].t; }
int main() {
    f<int, 2, &s>();
}

The problem here is that you can't use template argument deduction on the length of the array nor on its type, so both must be supplied as template parameters in addition to the array itself. I really think that passing in a struct or, say a vector would be the better solution, as you've no doubt already explored:

#include <vector>
#include <iostream>
template<typename T> struct S { T t; };
std::vector<S<int>> s{ { 21 }, { 22 } };
template<typename T, std::vector<S<T>> *v> void f() { std::cout << v->back().t; }
int main() {
    f<int, &s>();
}

Upvotes: 1

Related Questions