5

I have a boost::variant of types such as:

typedef boost::variant< uint8_t, int8_t, uint16_t, int16_t,
                uint32_t, int32_t, float, double, std::string > StorageTt;

A StorageTt variable, say val, is set to one of these storage types later in my code. I'd like to retrieve the type that val currently holds to define more variables of that same type. So if val is currently a uint16_t, I want to do something like:

typedef decltype(typeid(val)) StorageTt;
StorageTt new_val = 42;  // new_val should be a uint16_t

but this yields a const type_info type. I know I can do:

switch (val.which()) {
    case 0: // uint8_t
    case 1: //...

but I'd rather avoid the long switch-statement, because I have to do this multiple times.

Kevin
  • 16,549
  • 8
  • 60
  • 74

3 Answers3

2

You cannot do that. Variables are syntactic constructs. They are names for program objects. Names only exist in the source code.

The workflow is as follows. First you write the source. Then you compile it, run the program, and it executes some actions, for example, retrieves a value from a boost::variant object. At this point you cannot define any names. There are no source, no names, no syntax. There are only objects.

If you need a new object of the same type as the one retrieved from the variant, then StorageT new_val(val); creates just that (the new object is hidden in new_val, you access it with boost::get or boost::apply_visitor or whatever).

If you want to perform an action on whatever variant you've got, and all actions look the same and only their types are different (they are not the same because the types are different, they just look the same), and you want to avoid writing same-looking stuff multiple times, then of course templates are your friends. boost::apply_visitor with a template<typename> operator() is the right one:

struct my_assignment_visitor: public boost::static_visitor<>
{
    template <typename T> void operator()(T & var, double val) const
    {
        var = val;
    }
};

boost::apply_visitor(my_assignment_visitor(), new_val, 32.0);
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Sweet! The type T gives me the type I'm looking for (uint16_t, double, etc), and I can define new variables using that. Thanks again! – Kevin Sep 18 '14 at 16:54
  • 1
    You are defining all possible variables at once at compile time, not one at a time as new types are encountered at run time. But if it works for you, great. – n. m. could be an AI Sep 18 '14 at 17:19
2

You can do something like this using a visitor functor with a call operator template:

struct MyVisitor : public boost::static_visitor<>
{
    template <typename StorageT>
    void operator()(const StorageT&) const
    {
        StorageT new_val = 32; // declare variable with same type

        doSomethingWith(new_val); // do something with it
    }
};

Apply it to the variant val like so:

boost::apply_visitor(MyVisitor(), val);

Reference:

I'm not aware of a way to replace the functor with a C++14 generic lambda.

Oktalist
  • 14,336
  • 3
  • 43
  • 63
0

Using the copy constructor should work:

// construct object of same type as val
StorageT new_val(val);
boost::get(new_val) = 32.0;
egur
  • 7,830
  • 2
  • 27
  • 47
  • 1
    "How to get currently held variant type, and define new variables **of that type**" – Piotr Skotnicki Sep 18 '14 at 14:42
  • According to the code sample in the question, he asked for a variant of the same type and the copy constructor does just that. – egur Sep 18 '14 at 14:49
  • no, by assigning `32.0` to `new_val` you are changing its held type into `double`, and OP asks for `uint16_t` (which is not possible fyi). – Piotr Skotnicki Sep 18 '14 at 14:50