Template Casting

Pages: 12
Maybe this could help to be even clearer what i'm trying to do, i need to do a sort of lexical_cast from boost library, but with templates and in c++11

I didn't know the existence of this cast type until now, i just found it surfing on different pages, and it is a good example of what i'm trying to do
Last edited on
I don't understand. Are you trying to store values of different types in a single variable, or types of objects?

Store values of variable types:
1
2
3
4
5
6
7
8
9
Variant random_value(){
    if (coin_flip())
        return Variant(35.4);
    return Variant("hello world");
}

auto x = random_value();
double d = x;
std::string s = x;

Store types:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Metatype random_type(){
    switch (random(4)){
        case 0:
            return get_type<int>();
        case 1:
            return get_type<double>();
        case 2:
            return get_type<std::string>();
        case 3:
            return get_type<Foo>();
    }
}

auto type = random_type();
Variant unknown_thing = type.construct();
Last edited on
i need to do a sort of lexical_cast from boost library, but with templates and in c++11
lexical_cast() is a way to weaken the type system by coercing one type into another. The most common solution is to treat strings as the interchange type and convert all other types to and from strings. For example, this is how JavaScript originally implemented its types

What do you mean you want to use lexical_cast() "with" templates? How do you think using templates is going to help you?
I don't understand. Are you trying to store values of different types in a single variable, or types of objects?


I'm trying to store values of differents types.

How do you think using templates is going to help you?

Because my main program is structured with a template class, then i need to store this class objects into a single structure, but this structure can store values of all different templates that the object can be (e.g. int, float, char, date or time).

So i used a list of union type (I know that could be a bad idea as said before from @Repeater, nut with my c++ knowledge was the best solution) that can stores all values of this different types, and every time i need to use them i have a vector of tags (char) that helps me to know each time which value type has been saved into that specific union.

The only problem that i faced is that i can't make a direct assignment, thing that i need to do in a little part of my project.

So is something like list.begin()->union.obj_type1 = obj_template, this specific code line is what i summed in the first code i post.

I didn't explain this before because i know that probably is not the good way to do what i'm doing and could be just much confusing than before.
Last edited on
Then what you have right now is basically as good as it gets. The suggestion I posted yesterday with polymorphic classes is slightly better because the classes can ensure that you're not mismatching types when reading from the variant class, but at the storage level it's basically the same as a union.

Templates don't help you for this because templates work on values and types that are knowable at compile type, but the values you're trying to store depend on types that are only known at run time. For example, there's no template that's going to make this code behave as expected:
1
2
3
4
5
6
7
Magic x;
if (foo == 0)
    x = 3.141592;
else if (foo == 1)
    x = (std::string)"hello world";
else
    x = HttpConnection("example.com");
C++ is statically-typed. What you're trying to do would require a dynamically-typed language. I would suggest that you try to redesign your code so that you don't need a list of arbitrarily-typed values.
So you're saying that what i need to do is not possible or possible but to complicated because my problem is how i designed my program? So you're advice is to change my code, isn't it?
its possible but maybe you should just go C on it.

enum types{int,char,string, bjorks, whatevers};
struct
{
unsigned char mytype; //set to enum named type.
unsigned char mydata[1000]; //size to fit.
};

...
if(thing.mytype == int)
{
int* ip = (int*)thing.mydata;
*ip = 42;
}
and you can do that for any type where sizeof < array size in the struct, and you can make it more rich and complicated from there by making the struct into a class to handle the low level shenanigans quietly under the hood.

the basic ideas is simple: dump raw bytes of anything into a buffer of raw bytes and cast it around with brute force using the left over C functionality that any pointer can be cast to any other pointer type without much complaint. Its ugly, its risky, etc, but it works if you are careful and precise with it. Its pretty much the union hack, without the union, and equally unsafe and ugly and troublesome, but you gotta do what you gonna do.

Its possible but 'rather hackity', unsafe, 'retro', and possibly even just plain out 'bad'. There are a number of modern c++ features that are broken by doing this kind of thing, but they are things you won't be using here anyway. I mean, I am willing to do it, unless someone says not to if its on the job, but only if there is some extreme reason to need to do it. I am not convinced you have no alternative here. This kind of stuff sometimes 'needs to be' for things like pulling raw data from a transmission apart or packing it up into a byte blob (serialization) to send out. There are tools that do it for you, but guess what they be doing under the hood? :P
Last edited on
So you're saying that what i need to do is not possible or possible but to complicated because my problem is how i designed my program? So you're advice is to change my code, isn't it?
It's not that it's "not possible". You can always just use a generic pointer and keep track of the last thing it pointed to, if you absolutely have to. It's going to be a pain and error-prone, but it's possible. What I mean is that it's never going to be as pretty or as safe as in a dynamic language, because C++'s type system and syntax are simply not designed to deal with variables whose type may change during their lifetime.

And yes, unless you want to tells us in detail what you're doing and why you need to store multiple types into a single collection, I would suggest a redesign. There's almost always a solution that doesn't involve having a list of arbitrary unrelated types. In my experience, needing such things is a sign that something went wrong in some part of the design.
Definitely seams what i'm looking for, but i'll be honest, you're words about unsafety and bad choices added to the already got from @helios, doesn't make me feel really comfortable in doing this, then for sure i am not as careful and precise as i should be.

So i apreciate a lot your explanation, and i need to give my last card before drastically change my main program.

Instead, just define overloads:
1
2
3
4
5
6
7
8
9
10
11
void func(int x) {
    std::cout << "INT: " << x << '\n';
}

void func(double x) {
    std::cout << "DOUBLE: " << x << '\n';
}

void func(char x) {
    std::cout << "CHAR: " << x << '\n';
}

This as been written by @mbozzi in the previous page of this topic, and i think i undestand only now what he was saying to me. This type of overloading should works in my code?
And if it will works, could be a good option or will anyway being a bad idea because my entire code seams to be not really a good idea?

I'd really apreciate an honest answer because i need to understand if what i'm thinking could be bad or not. Thanks all! :)
Last edited on
It's not that it's "not possible". You can always just use a generic pointer and keep track of the last thing it pointed to, if you absolutely have to. It's going to be a pain and error-prone, but it's possible. What I mean is that it's never going to be as pretty or as safe as in a dynamic language, because C++'s type system and syntax are simply not designed to deal with variables whose type may change during their lifetime.

And yes, unless you want to tells us in detail what you're doing and why you need to store multiple types into a single collection, I would suggest a redesign. There's almost always a solution that doesn't involve having a list of arbitrary unrelated types. In my experience, needing such things is a sign that something went wrong in some part of the design.


I get your point, in other parts of the program in fact i used exactly what you said, generic pointers ecc..., unfortunately i didn't know that was a bad idea.
I'm so persistent because changing my design now would need a really big amount of work and definitely i'd rather avoid it.

However thanks you too for all your answers, and if my last purpose will be rejected i'll change my code as said!
I'm so persistent because changing my design now would need a really big amount of work and definitely i'd rather avoid it.


Think about how much time you have spent discussing this topic on the forum. Are you sure that when you solve this problem you won't run into another related problem? How much time will you spend refactoring your code when you need to add a new feature next month? How much time will you spend debugging new features because of seemingly esoteric problems that were hard to avoid because of your current design.

It will be easier to change your design now than it will be after another feature is added.

I almost always prefer refactoring code when I discover a poor design just to save headaches down the road. If the program is at the end of its life cycle, then just hack around it. But if maintenance is ahead, consider redesign.
AllesnadroTZ wrote:
[some code] has been written by @mbozzi

I can't tell you whether or not it applies to your problem because I have no clue what you're trying to do.
Last edited on
This type of overloading should works in my code?
Overloading works on static types. The compiler looks at the type it knows about of a name and determines which overload to call. The compiler can't decide overloads based on the run time types of objects.
For example,
1
2
3
4
5
6
7
8
9
10
11
void func(int);
void func(char);
void func(double);

std::vector<void *> v;
v.push_back(new int);
v.push_back(new int);
v.push_back(new double);
v.push_back(new char);
for (auto p : v)
    func(p);
This code will not compile, because the type of p is void * and there's no overload of func() that accepts a void * or a type that is implicitly convertible from void *. The compiler doesn't know that you stored two ints, a double, and a char in the vector. All it sees is four generic pointers.

Polymorphism does allow you to do something like that, though:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Variant{
public:
    virtual void func() = 0;
};

class Int : public Variant{
    int data;
public:
    void func() override{
        std::cout << "I'm an int! " << this->data << "\n";
    }
};

class Char : public Variant{
    char data;
public:
    void func() override{
        std::cout << "I'm a char! " << this->data << "\n";
    }
};

class Char : public Variant{
    double data;
public:
    void func() override{
        std::cout << "I'm a double! " << this->data << "\n";
    }
};

std::vector<std::unique_ptr<Variant>> v;
v.emplace_back(std::make_unique<Int>());
v.emplace_back(std::make_unique<Int>());
v.emplace_back(std::make_unique<Double>());
v.emplace_back(std::make_unique<Char>());
for (auto &p : v)
    p->func();
Last edited on
I almost always prefer refactoring code when I discover a poor design just to save headaches down the road. If the program is at the end of its life cycle, then just hack around it. But if maintenance is ahead, consider redesign.


Is at the end of is life cycle, or i will not get all this time being persistent. I can't say that you are not right, redesign it could be the better choice, but in this case would take weeks, but I'm considering this option
Last edited on
Well i'll try the polymorphism way. If will not solve my problems i'll stop asking and just redesign my program and my ideas.

For now i'll get all the answer which i was looking for, so thank you all again.
I'll set this topic as solved!
Topic archived. No new replies allowed.
Pages: 12