Reputation: 319
Suppose that I have a templated class, like so:
template<class T, class V>
class MyClass
{
public:
MyClass(std::string const& name){
s = name;
}
T return_var1() {
return var1;
}
std::string s;
T var1;
V var2;
};
And I'm going to use it as shown:
class AnotherClass {
public:
void some_func() {
MyClass<int, double> my_var("test");
std::pair<std::string, std::any> tmp_pair("test", my_var);
my_vars.insert(tmp_pair);
}
void some_other_func() {
auto tmp = my_vars["test"];
// Any way to call member function without using std::any_cast<...> ?
// For example, not like this:
// (std::any_cast<MyClass<int, double>>tmp).return_var1()
// But like this:
std::cout << tmp.return_var1();
}
private:
std::unordered_map<std::string, std::any> my_vars;
}
Is there any way to call member function of an object stored in std::any without knowing or using the type of the contained object? (without using std::any_cast<>() as written on the comments above)
Any solution is acceptable, e.g. inheriting MyClass from a base class with CRTP, using std::variant
and so on.
The reason I need this is because I want to use a templated function as such:
some_templated_function<decltype(my_var.var1)>();
// Or similarly,
some_templated_function<decltype(my_var.return_var1())>();
The purpose is to allow the user to register the types <int, double>
only once, and use the typenames extensively, wherever needed.
Upvotes: 2
Views: 749
Reputation: 15468
Yes, this is possible as long as the function that should be called has a known signature. I'm going to describe things using a more simple example as the one in your OP. Let's assume you have two classes that provide a member function greet
with a fixed signature:
struct A
{
void greet() const { std::cout<< "hello" << std::endl; }
};
struct B
{
void greet() const { std::cout<< "goodbye" << std::endl; }
};
Using std::any
, you can wrap any instances of these classes:
std::any a = A();
a = B();
In order to use the contained objects, however, you always have to know the exact type that is currently contained. This is unfortunate if you have numerous types or if the types are not defined by yourself (as in a library, for example). The basic approach to overcome this requirement is given in the following code:
struct any_caller
{
template<typename T>
any_caller(T object)
: any_(object)
, greet_([](std::any const& a){ return std::any_cast<T const&>(a).greet();})
{}
std::any any_;
void (*greet_)(std::any const&);
auto greet() const { return greet_(any_); }
};
The idea is the following:
std::any
. You can use this information to set up a lambda that performs the std::any_cast
and thereafter calls the desired function.The code above can then be used as follows:
int main()
{
any_caller c = A();
c.greet(); //prints "hello"
c = B();
c.greet(); //prints "goodbye"
}
This basic example can further be extended to support more than one function, leading to a technique that has been called Inheritance without pointers described here.
Upvotes: 1