Is there any way to cast a std::any containing a derived pointer to a base pointer, without knowing the derived type?
Asked Answered
H

3

11

Let's say I have a std::any object which may or may not contain a pointer to some derived class of a given base class B. Is there any way I can do something that:

  1. Returns a B *, if the std::any object holds something convertible to B *, or
  2. Throws an exception if not?

It seems like dynamic_cast and std::any_cast each provide one half of this functionality, but I don't see any way of putting the two together.

I'm aware that I can make this work in various ways that involve explicitly enumerating every type convertible to B *, but that's the mother of all DRY violations.


Sample use case:

std::vector<std::any> setupTools(const std::string & confFile)
{
  std::vector<std::any> myTools;

  auto conf = parse(confFile);

  for(std::string & wrenchInfo : conf["Wrenches"])
  {
    Wrench::setup(myTools, wrenchInfo);
  }    

  for(std::string & hammerInfo : conf["Hammers"])
  {
    Hammer::setup(myTools, hammerInfo);
  }

   // 25 more kinds of tools
}

Factory1::init(const std::vector<std::any> & tools)
{
  m_wrench = any_get<Wrench *>(tools);
  m_hammer = any_get<Hammer *>(tools);
  m_saw = any_get<Saw *>(tools);
}

Factory2::init(const std::vector<std::any> & tools)
{
  m_wrench = any_get<TorqueWrench *>(tools);
}

Factory3::init(const std::vector<std::any> & tools)
{
  m_saw = any_get<CordlessReciprocatingSaw *>(tools);
}

I don't want to include a bunch of boilerplate code listing every single kind of saw in existence just so I can grab a saw -- any Saw -- to use in Factory1.

Hix answered 5/11, 2019 at 19:15 Comment(10)
Can you give some explanation as to why you need to use std::any for this, and what you're trying to solve overall? This sounds like a very awkward problem and there's probably a better way around itIrinairis
At risk of stating the obvious, if you agree to only put B*s into the std::any object, and not derived class pointers, then that solves the problem quite easily.Montfort
@Brian: Doesn't fit my use case.Hix
If it doesn't fit your use case then can you explain your use case?Shekinah
Added sample use-case.Hix
If all of myTools are tools why not just have a Tool base class and make myTools a std::vector<std::unique_ptr<Tool>>?Shekinah
@AlanBirtles: That may be an option. I'd have to go back and add a useless base-class to everything -- there is no overlapping functionality between saws and hammers -- but it seems cleaner than anything else I've found.Hix
@DanielMcLaury: "there is no overlapping functionality between saws and hammers" Then why are they in the same array? There clearly is some logical commonality between them, because you seem to want to stick them in the same place. That is, I don't understand why myTools exists; why do you want to put what you claim are entirely dissimilar objects into it, then iterate over them to try to remember what you put there?Pancreatin
@NicolBolas: I don't gain anything by putting them in separate collections, because if I want to grab a CordlessReciprocatingSaw then whether I'm iterating over all the tools or all the saws I'm still iterating over a bunch of stuff that is not CordlessReciprocatingSaws.Hix
Is there no baseclass for all tools?Rebellion
G
6

This is unachievable. It is only possible to get an object out from std::any using exactly the type that was put inside. Thus, you must know the type to get anything out of it.

It seems that std::any does not fit your use case.

Gasometry answered 5/11, 2019 at 19:36 Comment(0)
C
2

I'm late to the party, just just came across this question looking for an answer myself.

Here's what I eventually worked out.

I needed to pass either a wxMenuItem*, or a pointer to some sort of control (all are derived from wxControl) to my function in an std::any. I know if I have a wxMenuItem* or a control based on flags. So ignore wxMenuItem* 's for now.

As my code only has to work with what a base class wxControl has, (and I imagine since your hammers share a common base class, you only want to do base hammer type stuff), I...

static_cast<wxControl*>(myCombo*)

at the calling point, and

auto myPtr {std::any_cast<wxControl*>(theAnyField)};

at the called point, and voila.

Comitia answered 10/10, 2020 at 4:39 Comment(1)
I imagine this has some downsides for certain use cases, but this is perfect for mine too. And, if you need to get the original type, just dynamic_cast the result of any_cast!Masteratarms
M
0

Adding a base class "Tool" that has an enum ToolType attribute can help out. Then using the ToolType you can decide on the cast.

Missie answered 26/11, 2019 at 22:7 Comment(2)
This can work, I've seen it done. But it's a huge code smell, I would not recommend it.Scrimmage
Yes I agree with you, it would be better to rethink the architecture.Missie

© 2022 - 2024 — McMap. All rights reserved.