Is it possible to specialize a namespace depending on the template argument?

70 Views Asked by At

This is what I would like to get. The caller does not have access to B namespace.

template<typename T>
A::T foo1(std::vector<std::uint8_t> const& data)
{
  return foo2<B::T>(data);
}

T is a type defined (in different ways) both in A and B. I tried to solve this problem but the only solution that seems feasible is to overload it like this:

    void foo1(A::typeX& out, std::vector<std::uint8_t> const& data)
    {
      out = foo2<B::typeX>(data);
    }

    void foo1(A::typeY& out, std::vector<std::uint8_t> const& data)
    {
      out = foo2<B::typeY>(data);
    }

It is a good solution but it requires a new function to be added every time a new type (say typeZ) needs to be handled (notice that passing the out parameter as a reference is the only way to overload it as I cannot make the overloaded functions return it directly since they would differ only for the returned type, and this is not permitted). Again, the caller only knows about A namespace, so it cannot call foo2() directly.

The standard of C++ I'm using is C++14 (experimental namespaces allowed)

I hope I,ve been clear enough. Thank you!

I tried to map the input/output type but I didn't end up with a working solution and neither me neither the rest of the team liked that.

I'm expecting to find a workaround to make the first code snippet working (of course, not exactly in the way I wrote it). In general, I'm trying to specialise a namespace: if the input parameter type is A::typeX then I use the B::typeX namespace, otherwise if the input parameter type is A::typeY then I use the B::typeY namespace.

Preferably (notice, preferably) I'd like a solution that doesn't require predefined mappings.

1

There are 1 best solutions below

0
Mestkon On

The only way to do this AFAIK is to use some kind of type map, either by using std::conditional or template specialization.

// std::conditional
template<class T>
using b_type_t = typename std::conditional<
    std::is_same<T, A::typeX>::value, B::typeX,
    typename std::conditional<
        std::is_same<T, A::typeY>::value, B::typeY, 
        ...>::type>::type;

// template specialization
template<class T>
struct b_type { };
template<class T>
using b_type_t = typename b_type<T>::type;

template<> struct b_type<A::typeX> { using type = B::typeX; }
template<> struct b_type<A::typeY> { using type = B::typeY; }
....

And then use the map in the functions

template<typename AType>
AType foo1(std::vector<std::uint8_t> const& data)
{
    return foo2<b_type_t<AType>>(data);
}

If the types in B are identical to the types in A then you can import the types into A instead of defining identical structs.

// import the entire namespace
namespace A {
using namespace B;
}

// or import only the types
namespace A {
using B::typeX;
using B::typeY;
....
}

// Then use the types directly
template<typename T>
T foo1(std::vector<std::uint8_t> const& data)
{
    return foo2<T>(data);
}