How to store type information received from the constructor at the class level that will be used during casting

I am trying to write a class that I can store and use type information without using a template parameter.

I want to write something like this:

class Example { public: template<typename T> Example(T* ptr) : ptr(ptr) { // typedef T EnclosedType; I want this be a avaialable at the class level. } void operator()() { if(ptr == NULL) return; (*(EnclosedType*)ptr)(); // so i can cast the pointer and call the () operator if the class has one. } private: void* ptr; } 

I am not asking how to write the is_functor () class.

I want to know how to get type information in a constructor and save it at the class level. If this is not possible, another solution will be evaluated.

+6
source share
3 answers

I see this as a good and correct question, however there is no general solution besides using the template parameter at the class level. What you tried to achieve in your question - using a typedef inside a function and then accessing it in the whole class is impossible.

Erase Styles

Only if you impose certain restrictions on your constructor options are there several alternatives. In this regard, here is an example of type erasure in which the operator() some given object is stored inside the variable std::function<void()> .

 struct A { template<typename T> A(T const& t) : f (std::bind(&T::operator(), t)) {} void operator()() const { f(); } std::function<void()> f; }; struct B { void operator()() const { std::cout<<"hello"<<std::endl; } }; int main() { A(B{}).operator()(); //prints "hello" } 

Demo

Note, however, the assumptions underlying this approach: it is assumed that all past objects have an operator of this signature (here void operator() ), which is stored inside a std::function<void()> (relative to the storage of the function- member, see here ).

Inheritance

In a sense, erasing a type thus looks like "inheritance without a base class" - instead, you can use a common base class for all constructor parameter classes with a virtual bracket operator, and then pass the base class pointer to your constructor.

 struct A_parameter_base { void operator()() const = 0; }; struct B : public A_parameter_base { void operator()() const { std::cout<<"hello"<<std::endl; } }; struct A { A(std::shared_ptr<A_parameter_base> _p) : p(_p) {} void operator()() { p->operator(); } std::shared_ptr<A_parameter_base> p; } 

This is similar to the code in your question, only that it does not use void -pointer, but a pointer to a specific base class.

Both approaches, type erasure, and inheritance are similar in their applications, but type erasure may be more convenient as you get rid of the common base class. However, the inheritance approach has the added benefit that you can restore the original object using multiple submit

It also shows the limitations of both approaches. If your statement is not void , but instead returns an unknown different type, you cannot use the above approach, but you must use templates. Parallel inheritance: you cannot have a virtual function template.

+4
source

The practical answer is to save either a copy of your class or std::ref wrapped pseudo-link to your class in std::function<void()> .

std::function type erases things that it stores up to three concepts: copy, destroy and call with a fixed signature. (also, casting back to the original type and type is more obscure)

What he does is remember when building how to perform these operations on the transferred type and save a copy so that she can perform these operations on it, and then forgets everything else about the type.

You cannot remember everything about a type in this way. But almost any operation with a fixed signature, or that can be intermediate using a fixed signature operation, can be erased to type.

The first typical way to do this is to create a private clean interface with these operations, and then create an implementation of the template (template for the type passed to ctor) that implements each operation for this particular type. A class that does type erasure then saves a (smart) pointer to a private interface and redirects its public operations to it.

The second typical way is to store a void* or char buffer, as well as a set of pointers to functions that implement operations. Pointers to functions can either be stored locally in the type erasure class, or stored in an auxiliary structure that is created statically for each erased type, and a pointer to the auxiliary structure is stored in the type erasure class. The first way to store function pointers is as properties of a C-style object: the second is as a manual vtable.

In any case, function pointers usually take one (or more) void* and know how to return them to the desired type. They are created in ctor, which knows the type, either as instances of the template function, or as local nuclear-free lambdas, or indirectly.

You can even make a hybrid of two: pimpl instance static pointers accepting void* or something else.

Often the use of std::function enough, manual erasure of the text type is difficult to obtain compared to using std::function .

+1
source

Another version for the first two answers that we have here is that is closer to your current code:

 class A{ public: virtual void operator()=0; }; template<class T> class B: public A{ public: B(T*t):ptr(t){} virtual void operator(){(*ptr)();} T*ptr; }; class Example { public: template<typename T> Example(T* ptr) : a(new B<T>(ptr)) { // typedef T EnclosedType; I want this be a avaialable at the class level. } void operator()() { if(!a) return; (*a)(); } private: std::unique_ptr<A> a; } 
+1
source

Source: https://habr.com/ru/post/988414/


All Articles