Home > Back-end >  One liner template type changer
One liner template type changer

Time:08-08

I'm sorry for the title, I don't know how to call it...

I'm trying to make a one liner of the caller() function (C 20).

template <typename T>
void func() {
    std::cerr << typeid(T).name() << std::endl;
}

void caller(const int &_i) {
    switch(_i) {
        case 1:
            func<uint8_t>();
            break;
        case 2:
            func<uint16_t>();
            break;
        case 3:
            func<uint32_t>();
            break;
        case 4:
            func<uint64_t>();
            break;
    }
}

int main() {
    caller(1);
    caller(2);

    return 0;
}

I tried with struct helper but it seems this case do not fit.
I also tried a different approach with caller<i>() and the use of std::conditionnal_t, but did not find how to use it in this specific case.

template <size_t ID>
void func() {
    using T = std::conditionnal_t<ID==0, uint8_t, [...]>;
    std::cerr << typeid(T).name() << std::endl;
}

Is there multiple ways to do this, and if so, which are they?

CodePudding user response:

This is as close as I can get to caller being a branchless one liner (use with care, no out of bound checks)

#include <array>
#include <functional>
#include <iostream>

template <typename T>
void func() 
{
    std::cout << typeid(T).name() << "\n";
}

static constexpr std::array<void(*)(), 4> funcs
{
    func<std::uint8_t>,
    func<std::uint16_t>,
    func<std::uint32_t>,
    func<std::uint64_t>
};
    
void caller(const std::size_t index)
{
    funcs[index]();
}

int main()
{
    caller(1ul);
    return 0;
}

CodePudding user response:

If you can provide the value to caller at compile time, you can do something like this:

template<std::size_t>
struct int_type_id;

template<>
struct int_type_id<0> { using type = std::uint8_t; };

template<>
struct int_type_id<1> { using type = std::uint16_t; };

template<>
struct int_type_id<2> { using type = std::uint32_t; };

template<>
struct int_type_id<3> { using type = std::uint64_t; };

template <size_t ID>
void caller() {
    using T = typename int_type_id<ID>::type;
    func<T>();
    // you can reduce that to a one liner:
    // func<typename int_type_id<ID>::type>();
}

Then call it like that:

int main() {
    caller<1>();
    caller<2>();

    return 0;
}

CodePudding user response:

Using std::variant and std::type_identity to do type dispatch.

using VType = std::variant<std::type_identity<uint8_t>,
                           std::type_identity<uint16_t>,
                           std::type_identity<uint32_t>,
                           std::type_identity<uint64_t>>;

static const std::unordered_map<int, VType> dispatcher = {
    {1, std::type_identity<uint8_t>{}},
    {2, std::type_identity<uint16_t>{}},
    {3, std::type_identity<uint32_t>{}},
    {4, std::type_identity<uint64_t>{}},
};

void caller(const int &_i) {
    return std::visit([&](auto v){
        func<typename decltype(v)::type>();
    }, dispatcher.at(_i));
}

Demo

Disclaimer: Pay attention to run-time performance if you care about it.

  • Related