Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 919
Oleg Efremov Avatar asked Feb 22 '26 06:02

Oleg Efremov


2 Answers

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.

like image 67
Konrad Rudolph Avatar answered Feb 24 '26 21:02

Konrad Rudolph


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>();
}
like image 33
ecatmur Avatar answered Feb 24 '26 20:02

ecatmur



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!