6

In Java you can create a Map that maps String to Generic Object types that can be explicitly casted to other classes. Is there any nice way of imitating this functionality in C++?

6
  • There's a map in c++11/14, std::map. please check if it could meet your requirement. Commented Aug 17, 2017 at 4:11
  • 1
    i think op is talking about a class Object that can accept any kind of object...not about the data container collection Map Commented Aug 17, 2017 at 4:20
  • 2
    You might be looking for std::any. Or perhaps for a plain old void*. Difficult to tell without knowing how you plan to use this thing. Commented Aug 17, 2017 at 4:23
  • 2
    This is really a lot more open ended of a question than it might appear at first glance. There's no direct correlate in C++ to Java Map<String, Object>, because the C++ typesystem itself is too different in fundamental ways. For example, Map<String,Object>, being a java generic, is really fundamentally Map<Object,Object> with syntactic sugar; and is a base class. std::map<std::string, Foo> in C++ is a first level object, which genuinely has std::string keys. Also, there's no Object equivalent in C++, though there are weak types/wrappers. Commented Aug 17, 2017 at 4:33
  • 1
    ...if there's a particular itch you're scratching with Map<String,Object> in Java, there's likely at least one particular way to scratch that itch in C++; generally though there's at least 5 different approaches to "mimic" this in C++ that I can think of off the top of my head (and they're all different). Commented Aug 17, 2017 at 4:36

2 Answers 2

3

In C++17 you can use std::map<std::string, std::any>.

Sign up to request clarification or add additional context in comments.

Comments

1

Being a quite strongly typed language, C++ does not have a "Generic Object Type". It surely has associative containers: std::map (a flavour of binary tree) and std::unordered_map (a flavour of hash table). Which is better depends on the use case, and often can't be decided without profiling.

The closest thing to a generic object I can think of is a common ancestor for all the objects that might be put in this map. Here the idea is to create a class hierarchy with dynamic polymorphism, and to store the objects in the map as pointers cast into that common ancestor. The ideal design would make casting of these objects back to their derived class unnecessary. If instead such cast is required, one will have to use a dynamic_cast (and possibly check that it succeeded).

It is mandatory to store pointers to the objects in the map, as opposed to the objects themselves. Otherwise, of the objects one tries to insert into the map, only the common ancestor part would be stored, and the polymorphism would be lost. It also needs to be decided whether the map owns the objects or not (no garbage collection here). If not, simple pointers may work. If the map owns the objects, I would recommend storing them wrapped in "unique pointers" (std::unique_ptr). Wrapping up:

#include <unordered_map>
#include <string>
#include <memory> // std::unique_ptr<>, std::make_unique()
#include <iostream>

class NotSoGenericClass {
    public:
  virtual ~NotSoGenericClass() = default;
  virtual std::string name() const
    { return "NotTooGenericClass()"; }
};

class EvenLessGenericClass: public NotSoGenericClass {
  int fValue = 0;
    public:
  EvenLessGenericClass(int value): fValue(value) {}
  virtual std::string name() const override
    { return "EvenLessGenericClass(" + std::to_string(fValue) + ")"; }
  int value() const { return fValue; }
};

int main() {
  //
  // map holding (and owning) "not so generic objects"
  //
  std::unordered_map<std::string, std::unique_ptr<NotSoGenericClass>> allObjects;

  //
  // populate it
  //
  allObjects["any"] = std::make_unique<NotSoGenericClass>();
  allObjects["six"] = std::make_unique<EvenLessGenericClass>(6);
  allObjects["one"] = std::make_unique<EvenLessGenericClass>(1);

  std::cout << "Object 'six' says: " << allObjects["six"]->name() << std::endl;

  std::cout << "Now dumping all " << allObjects.size() << " objects:";
  for (auto const& keyAndObject: allObjects) {

    auto const& key = keyAndObject.first;
    auto const* object = keyAndObject.second.get();

    //
    // base class interface is always available:
    //
    std::cout << "\n[" << key << "] " << object->name();

    //
    // object-specific one requires a cast:
    //
    auto const* lessGen = dynamic_cast<EvenLessGenericClass const*>(object);
    if (lessGen) std::cout << " (value is " << lessGen->value() << ")";

  } // for
  std::cout << std::endl;

  return 0;
} // main()

On my platform, this code (using C++14) emits:

[one] EvenLessGenericClass(1) (value is 1)
[six] EvenLessGenericClass(6) (value is 6)
[any] NotTooGenericClass()

(also illustrating the meaning of "unordered" in the map name). This example was compiled with g++ -Wall -pedantic -std=c++14 -o test.exe test.cpp (GCC 6.4.0).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.