1

A bit of an obscure issue here but I need a way to cast std::any to its base class, without knowing what derived class it is.

In other words, given a base class:

struct HCallable {
    int numArgs;
    std::vector<std::type_info> argTypes;
    virtual std::any call(std::vector<std::any> args)=0;
};

And an instance of one of its (numerous) derived classes:

class in : public HCallable {
public:
int numArgs=1;

std::any call(std::vector<std::any> args) {
    std::cout << huff::anyToString(args[0]);
    std::string result;
    std::getline(std::cin, result);
    return result;
}
};

std::any instance = std::any(new in()); 

How can I take this instance, and convert it into an object of type HCallable without knowing that instance has type in.

This code works if only I can find a way not to explicitly state in*:

HCallable* func =  dynamic_cast<HCallable*>(std::any_cast<in*>(instance));
Gamaray
  • 51
  • 8
  • Do you control the base classes? I have a trick in mind, but it requires adding a certain macro to each base class that needs to work as a cast target. – HolyBlackCat Apr 15 '23 at 10:37
  • 2
    Why not make the destructor of `HCallable` virtual and go with `std::unique_ptr instance = std::make_unique();`??? Btw: Since your question seems to be entirely unrelated to the signature of `call`, I recommend removing `std::any` from that signature; this makes it easier to determine the `std::any` use you're actually asking about... – fabian Apr 15 '23 at 10:38
  • [Related](https://stackoverflow.com/q/58718012/10147399)? – Aykhan Hagverdili Apr 15 '23 at 10:47
  • "_How can I take this instance, and convert it into an object of type HCallable without knowing that instance has type in._": You can't. The point of `std::any` is to store _arbitrary_ (copyable) types that are not related by inheritance. What should happen if the type contained isn't even inherited from `HCallable`? The only thing `std::any` allows you to do without knowing the contained type is to copy it. For storing inheritance-related types, you should probably be using `std::unique_ptr` or `std::function` or some custom variation of it, depending on exact use case. – user17732522 Apr 15 '23 at 12:33
  • In general, `std::any` (as well as `std::type_info`) is only very rarely useful. If you find yourself using it regularly, then your general approach is probably problematic. In particular I do not think that `HCallable` as you are defining it is a good idea. How is it intended to be used? – user17732522 Apr 15 '23 at 12:37
  • @user17732522 This is part of a tree-walk interpreter for a language I am working on called Huffle - ```HCallable``` is used to represent user defined & naitve functions. When the user defines a new function - an instance of ```HCallable``` (stored as ```any```) is added to the environment, when this is accessed using the call operator I need to cast the instance back so that the appropriate code can be executed. Sadly I have no choice but to use ```std::any``` (I believe most interpreters do) since there are many different types being stored in the same containers constantly... – Gamaray Apr 15 '23 at 17:05
  • @HolyBlackCat Yes I do, I would be interested to see what you have in mind – Gamaray Apr 15 '23 at 17:07
  • @Gamaray Types in your language have nothing to do with types in the host language. Even if the user can define their own types, you still have only a finite and controlled set of possible types in the host language and you can use a `std::variant` with `std::visit` or a shared base class with virtual function interface to represent these different types in the AST nodes. `std::any` is not needed or particularly useful for that. – user17732522 Apr 15 '23 at 18:12
  • I did look into ```std::variant``` - retrospectively perhaps that would been a better option - the thing is there are thousands of lines of code now so changing it to have a more concrete type system as you suggest would be a bit of an undertaking - I could separate the scope environments into variables who's values could remain ```std::any``` and callables for which I could then do as you suggest... Or would this be bad practice? – Gamaray Apr 15 '23 at 19:07

1 Answers1

-2

Here's how you can convert an object of an unknown derived class to its base class type using std::any_cast and dynamic_cast.

First, you need to extract the stored pointer to the derived class object from std::any. You can do this by using std::any_cast with a pointer to the base class HCallable and then you can use dynamic_cast to safely convert the pointer to the base class pointer type HCallable*.

Here is example you can do something like this :

std::any instance = std::make_any<DerivedClass>();

// Extract derived class object from std::any
auto derivedPtr = std::any_cast<DerivedClass*>(&instance);

// convert the pointer to the base class
HCallable* basePtr = dynamic_cast<HCallable*>(*derivedPtr);

// Check if pointer conversion completed before using basePtr
if (basePtr) {
    // Here you can use basePtr as an HCallable*
} else {
    // error
}
  • This is exactly what the op wanted to avoid. They want to cast to base without knowing what type derived is. – chrysante Apr 15 '23 at 11:28