C++ Logo

std-proposals

Advanced search

Re: std::any / std::any_cast without typeid

From: Nikolay Mihaylov <nmmm_at_[hidden]>
Date: Sun, 6 Dec 2020 00:31:29 +0200
@Arthur
Thanks Arthur!

I am watching your videos as soon as you post them :)

@all

I can see that the idea for storing numbers e.g. floats / doubles / ints
can not be possible, because the "internal" type is not known.

Extracting base class would work, if std::any holds child class, but only
when single inheritance is made.
However this is implementation detail so the fact it works for a specific
compiler does not mean it will always work.
It will *NOT* work with multiple inheritance. this example returns garbage:

// https://gcc.godbolt.org/z/zG87Tz

struct X{
virtual int xprocess() = 0;
};

struct A{
virtual int process() = 0;
};

struct B : X, A{
int process() override{
return 5;
}

int xprocess() override{
return 54;
}
};

int main(){
B b;

void *v = &b;

auto *x = (A *) v;
// this would be correct
//auto *x = (A *) (B *) v;

return x->process();
}




On Sat, Dec 5, 2020 at 11:21 PM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
wrote:

> On Sat, Dec 5, 2020 at 3:49 PM connor horman via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> On Sat, Dec 5, 2020 at 14:31 Nikolay Mihaylov via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>> Consider following code:
>>>
>>> struct A{
>>> virtual ~A() = default;
>>> virtual int op(int a, int b) const = 0;
>>> };
>>>
>>> struct Sum : A{
>>> int op(int a, int b) const override{
>>> return a + b;
>>> }
>>> };
>>>
>>> #include <any>
>>>
>>> std::any x = Sum{};
>>>
>>> int main(){
>>> return std::any_cast<A &>(x).op(5, 5);
>>> }
>>>
>>> It crashes with `std::bad_any_cast` exception. [...]
>>>
>> Maybe we need some other kind of cast, for example
>>> *std::non_secure_any_cast* that just casts the value no matter what.
>>>
>>
> You need to distinguish between different kinds of "casts." Some are
> physically possible to implement; some aren't.
> For example, it's trivial to implement a reinterpret_any_cast<T>. But that
> won't help you in situations like
>
> struct A { int i = 1; };
> struct B { int j = 2; };
> struct C : A, B { int k = 3; };
> std::any x = C{};
> int main() { return reinterpret_any_cast<B&>(x).j; } // returns 1, not 2
>
> It's also possible to implement a dynamic_any_cast, exploiting the special
> fact that std::any always owns its contents and therefore knows (at
> construction time) the *most derived type* of the contained object. For
> this implementation, see the worked example in "Back to Basics: Type
> Erasure" <https://www.youtube.com/watch?v=tbUCHifyT24&t=36m59s> (CppCon
> 2019); and for the detailed semantics of dynamic_cast and the significance
> of knowing the *most derived type*, see "dynamic_cast from scratch"
> <https://www.youtube.com/watch?v=QzJL-8WbpuU> (CppCon 2017). However,
> dynamic_cast works only on polymorphic types.
>
> struct A { int i = 1; };
> struct B { int j = 2; virtual ~B(); };
> struct C : A, B { int k = 3; };
> std::any x = C{};
> int main() { return dynamic_any_cast<B&>(x).j; } // OK, returns 2
>
> However, from your motivation, it seems like you are really asking for the
> ability to do, like,
>
> std::any x = 3.14;
> int main() { return static_any_cast<float>(x); } // could this return
> 3.14f? NO!
>
> and that's simply physically impossible. (Again, see "Back to Basics: Type
> Erasure" for the details.)
>
> HTH,
> –Arthur
>

Received on 2020-12-05 16:32:08