#How do you deduce the type T from its std::type_info metadata = typeid(T(args...)) ?

81 messages · Page 1 of 1 (latest)

grizzled stratus
#

This hack is essential for type erasure, and then for implementing void* wrappers with strict type information. (No typed unions here)

grim coyoteBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

left ermine
#

wdym

#
template<typename>
struct S;

template<typename T, typename... args>
struct S<T(args...)> {
    // use T
};

// ...

S<int(float, char)>
```?
grizzled stratus
#

in C++11

left ermine
#

hm

grizzled stratus
# left ermine hm

you know that any_cast we have to check "does this std::any instance contain the type T1 in it or not?"

left ermine
#

yeah it does check for that

grizzled stratus
#

that's what I am looking for

left ermine
#

interesting

grizzled stratus
#

the answer there is

#
template<typename T>
T* any::any_cast()
{
  if(typeof(T) == this->metadata)
  {
    return reinterpret_cast<T*>(this->rawPtr);
  }
  return nullptr;
}
#

which is ez

#

but, deducing what T really is in order to run the destructor of std::any and do CORRECT cleanup with ~T() without leaks

#

is insane and I don't know how

gusty crescent
#

There has to be compiler magic. But then it doesn't feel right.

buoyant swan
#

It's not that difficult if you know the trick.

struct Any {
  void *p;
  void (*m_delete_T)(void*);
};

template <class T>
void delete_T(void *p) {
  delete reinterpret_cast<T*>(p);
}

Any a;
a.p = new int;
a.m_delete_T = &delete_T<int>;
#

Obviously you encapsulate the last 2 things into the assignment operator so people can't put different types in there.

grizzled stratus
buoyant swan
#

Yes

#

Fixed

grizzled stratus
# buoyant swan Yes

but the template instantiations of delete_T will be limited, users of this Any want their own instantiations?

buoyant swan
#

What do you mean?

grizzled stratus
#

wait let me write the constructor

grizzled stratus
buoyant swan
#
template <class T>
void delete_T(void *p) {
  delete reinterpret_cast<T*>(p);
}

struct Any {
  template <class T>
  auto operator=(T &&t) {
    p = new T(std::forward<T>(t));
    m_delete_T = &delete_T<T>;
  }
  ~Any() {
    m_delete_T(p);
  }
  private:
  void *p;
  void (*m_delete_T)(void*);
};
buoyant swan
#

Missing some things obviously, but the important part is there.

grizzled stratus
# buoyant swan Yes

and any attempt to fill in a different T will force the C++ compiler to generate the helper function for the deletion, and we take its address

buoyant swan
#

Yup

grizzled stratus
#

to do type checks?

buoyant swan
#

Yeah, to check whether the given T and the stored type match.

grizzled stratus
#

!SOLVED

#

!solved

grim coyoteBOT
#

Thank you and let us know if you have any more questions!

This thread is now set to auto-hide after an hour of inactivity

grizzled stratus
# buoyant swan Yeah, to check whether the given `T` and the stored type match.

oh yeah, also
C devs writing OOP be like "yeah so we need a function pointer in the fields which takes the thisPtr pointer as a first param"
C++ devs writing OOP be like "let's add a public member function with a hidden implicit this parameter"
C++ devs reimplementing Any be like "return to monke, insert the custom deleter as a function pointer field 🐒 "

#

🤣

buoyant swan
#

Well, you could use std::unique_ptr with a custom deleter instead if you wish to reject 🐒

#

Seems more complicated though.

grizzled stratus
buoyant swan
#

You can probably also abuse std::function to make it somewhat nicer.

grizzled stratus
buoyant swan
grizzled stratus
buoyant swan
#

Well, you can, but you need to type-erase it, which kind of defeats the purpose.

grizzled stratus
#

but shared_ptr can be hacked to use shared_ptr<void> p(new T(), CustomDeleter())

buoyant swan
#

Still feels like caveman pointers are the reasonable way to go here.

grizzled stratus
#

😄

buoyant swan
#

I had to censor this to not allow people to quote me saying "raw owning pointers are the way to go".

grizzled stratus
buoyant swan
#

N-nobody!
-# they are watching

gusty crescent
#

So I still don't get how

shared_ptr_to_base(shared_ptr_to_derived, static_cast<base*>(shared_ptr_to_derived.get()))```
Work..
grizzled stratus
gusty crescent
#

It says that the control block of derived_ptr is copied that includes the deleter.

gusty crescent
buoyant swan
#

Which makes sense, since the deleter's signature does not appear in std::shared_ptr's template parameters.

#

Unlike for std::unique_ptr where it does.

gusty crescent
buoyant swan
#

Basically, but you change it to void* instead of T*.

#

Doesn't matter for calling it, but makes it so they are all compatible with each other.

gusty crescent
#

Thanks for the explanation 👍

buoyant swan
#

It's not that much text, but I don't feel like reading through it.

grizzled stratus
#

showcasing the deleter lifetime

buoyant swan
#

What's with that formatting? Can't even see it all. But I think I get the gist of it.

#

It's a neat trick. Also comes up elsewhere, like when you make an ECS.

#

Though often you can make std::function do the work for you.

grizzled stratus
buoyant swan
#

You end up with

struct Entity {
  std::size_t entity_id;
  ~Entity(); //delete all components attached to this Entity
};

so you have to type-erase all the components and delete them somehow.

#

It's made worse because you have an arbitrary number of components that independently get added or removed.

grizzled stratus
grizzled stratus
#

What can reinterpret_cast docs say about what you have done here?

buoyant swan
#

I remember a rule that pointers are void * roundtrip-castable, meaning you can cast any pointer to void * and back and it'll still work. Meaning void * cannot really be shorter than other pointers.