2

I'm trying to create a type that would allow me to do something like that :

constexpr const char x[] = "x";
constexpr const char y[] = "y";
int main()
{

    Type<_Type<int, x>, _Type<float, y> > truc;
    std::cout << truc.get<std::forward<const char*>(x)>() << std::endl;
}

_Type (which will be renamed) should contains code to get data (using template parameters) from a database (the member variable are static because it was easier to do the tests like that). Type (which will be renamed too) is used to tie together a bunch of _Type.

With x and y as local variable, gcc says that x and y have no linkage.

The type implementation is :

constexpr bool strings_equal(char const* a , char const* b )
{
    return *a == *b && (*a == '\0' || strings_equal(a + 1, b + 1));
}

template <typename T, const char* n>
struct _Type {
    using type = T;
    static constexpr T value = T{};
    static constexpr const char* name = n;
    template <const char* name, typename = typename std::enable_if<strings_equal(n, name)>::value>
    T get()
    {
        return value;
    }
};
template <typename T>
struct _Type2 : T {
};
template <class... T>
struct Type {
    std::tuple<T...> vals;
    template <unsigned idx, const char* name, bool end_ = true>
    auto _get() -> typename decltype(std::get<idx + 1>(vals))::type
    {
        return decltype(std::get<idx + 1>(vals))::value;
    }
    template <unsigned idx, const char* name, bool end_>
    auto _get() -> decltype(
        _get<(idx + 1), std::forward<const char*>(name), strings_equal(name, std::remove_reference<decltype(std::get<idx + 1>(vals))>::type::name)>())
    {
        return _get<idx + 1, std::forward<const char*>(name), string_equal(name, std::remove_reference<decltype(std::get<idx + 1>(vals))>::type::name)>;
    }

    template <const char* name>
    auto get() -> decltype(
        _get<0, std::forward<const char*>(name), strings_equal(name, std::remove_reference<decltype(std::get<0>(vals))>::type::name)>())
    {
        return _get<0, name, string_equal(std::forward<const char*>(name), decltype(std::get<0>(vals))::name)>;
    }
};

This code yield an error (during compilation).

clang gives me this error:

test.cpp:60:23: error: no matching member function for call to 'get'
    std::cout << truc.get<std::forward<const char*>(x)>() << std::endl;
                 ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:48:10: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'name'
    auto get() -> decltype(
         ^
1 error generated.

Complete code and gcc error is available here.

What can I do to resolve this error and to be able to use local variable for the non-type template parameter ?

Thank you for your answers.

EDIT Using local string won't work due to their linkage (thanks @PasserBy for the explanation)

EDIT 2 Solved, thanks to @Jarod42

nefas
  • 1,120
  • 7
  • 16

1 Answers1

1

Local variables can never have external linkage. Ever. As to why linkage is important, see this.

The gist is that only c-strings of external linkage have the same value across translation units, and only then would the parameter be the same across translation units.

The following code compiles

constexpr const char x[] = "x";
constexpr const char y[] = "y";

template<typename T, typename U>
struct Type {};

template<typename T, const char*>
struct _Type {};

int main()
{
    //good, external linkage
    Type<_Type<int, x>, _Type<float, y>> t1;

    //won't compile, string literal has internal linkage
    //Type<_Type<int, "x">, _Type<float, "y">> t2;

    static constexpr const char local[] = "x";
    //won't compile, local variables don't have external linkage
    //Type<_Type<int, local>, _Type<float, local>> t3;
}
Community
  • 1
  • 1
Passer By
  • 19,325
  • 6
  • 49
  • 96