2

How to implement a class that dynamically switching types using std::variant in runtime?

I'm currently trying to create a class that can:

  • Store a value in a std::variant with configurable type options (in my case double and float)
  • Dynamically switch the active type at runtime using a setType<T>() method
  • Be callable as a function to convert inputs to the currently active type

Here's my current attempt of implementation:

#include <variant>
#include <iostream>
#include <typeinfo>

class TypeAliasClass {
public:
    TypeAliasClass() {
        value = float(1);
    }
    template <typename T>
    auto operator()(T transformable) {
        return std::visit([&](const auto& active_value) {
            using ActiveType = std::decay_t<decltype(active_value)>;
            
            if (std::is_convertible_v<T, ActiveType>) {
                return static_cast<ActiveType>(transformable);
            } else {
                std::cerr << "Couldn't convert given parameter to active type(" 
                          << typeid(ActiveType).name() << ")\n";
                return ActiveType{};
            }
        }, value);
    }
    
    template<typename T>
    void setType() {
        value = T(1);
    }
    
    ~TypeAliasClass() {
        std::cout << "Destroying TypeAliasClass with active type: " 
                  << std::visit([](auto&& arg) { 
                         return typeid(arg).name(); 
                     }, value) 
                  << std::endl;
    }

private:
    std::variant<float, double, int> value; // int was included as an exmaple
};

Example Usage:

TypeAliasClass converter;

converter.setType<int>();
std::cout << converter(3.14) << "\n";  // Returns 3
converter.setType<double>();
std::cout << converter("69") << "\n";  // Error
13
  • 1
    std::variant<std::type_identity<Ts>...> seems more appropriate, as you don't store value. Commented Apr 22 at 12:27
  • 2
    return type of auto operator() cannot depend of runtime value (type stored in the variant). Commented Apr 22 at 12:28
  • 1
    if should be if constexpr though. Commented Apr 22 at 12:30
  • 1
    The static type of a non-templated expression is fixed. You can't have converter(3.14) return two different types on different invocations. This is fundamental to C++. Commented Apr 22 at 12:34
  • 2
    godbolt.org/z/bxK5Ed43Y Commented Apr 22 at 12:51

2 Answers 2

5

std::variant<std::type_identity<Ts>...> seems more appropriate, as you don't store value.

Return type of template <typename T> auto operator()(T transformable) is problematic, as it should not depend of runtime value (content of the variant).

Instead you could have a apply/visit function, something like:

template <typename... Ts>
class TypeAliasClass {
public:
    TypeAliasClass() = default;

    template<typename T>
    void setType() { typeVar = std::type_identity<T>(); }

    template <typename F, typename T>
    decltype(auto) apply(F&& f, T transformable) {
        return std::visit([&]<typename ActiveType>(std::type_identity<ActiveType>) {
            if constexpr (std::is_convertible_v<T, ActiveType>) {
                return f(static_cast<ActiveType>(transformable));
            } else {
                std::cout << "Couldn't convert given parameter to active type(" 
                          << typeid(ActiveType).name() << ")\n";
                throw std::runtime_error("Incompatible type");
            }
        }, typeVar);
    }
    
private:
    std::variant<std::type_identity<Ts>...> typeVar;
};

int main()
{
  TypeAliasClass<float, double, int> converter; // type is the first one: float

  converter.setType<int>();
  converter.apply([](auto v){ std::cout << v << "\n"; }, 3.14);  // print 3
}

Demo

Sign up to request clarification or add additional context in comments.

Comments

0

I wanted a single function to return either float or double based on a runtime condition (e.g. a precision flag). After trying various approaches, including suggestions from comments and other answer, I realized there’s no magic pill for this in C++

The closest I got was using decltype with a ternary operator:

decltype(use_double_precision ? 1.0 : 1.0f)

However, this still resolves at compile time.

C++’s type system demands precise, compile-time-known types. I learned that lesson, and won't do the same mistake again.

4 Comments

decltype(use_double_precision ? 1.0 : 1.0f) is double unconditionally.
Yes, I know. As I mentioned earlier, that’s the closest I could get, but the compiler still generates code with double as the type there.
@Jarod42 That still requires a condition to be defined at compile time

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.