Having extreme difficulty with determining the correct value type for std::map

140 views Asked by At

Addendum:

Let me simplify my question, to try this one step at a time, because I feel I've created too much confusion about what I'm trying to achieve.

Mind you, I'm using C++11 and I think I'm bound to using this when using the PHP-CPP library. But, as I'm a total beginner, I'm not even sure about this.

My ultimate goal would be to "simply" create a map that can hold any value that the template Php::Class returns. But let's just start by trying a concrete one, for instance Php::Class<Animal::Mammal>.

First, I create a class in the namespace Animal called Mammal, which extends from Php::Base (when you want to export a class to PHP with PHP-CPP, it needs to extend Php::Base):

namespace Animal {
  class Mammal : public Php::Base { /* left out for brevity */ }
}

Then, to keep it simple, let me just try to create a map inside get_module() for now and just try to add an element there:

extern "C"
{
  PHPCPP_EXPORT void *get_module() {    
    // create the map
    std::map<std::string, Php::Class<Animal::Mammal>> classMap;

    // create the thing (at this point I just don't know what the actual return type is anymore) that will be exported later on.
    Php::Class<Animal::Mammal> mammal( "Mammal" );

    // try to add this "thing" to the map:
    classMap[ "Animal::Mammal" ] = mammal;

    /* left out for brevity */
}

This results in:

error: use of deleted function ‘Php::Class<Animal::Mammal>&
       Php::Class<Animal::Mammal>::operator=(const Php::Class<Animal::Mammal>&)’

When I try:

// require a pointer
std::map<std::string, Php::Class<Animal::Mammal>*> classMap;

classMap[ "Animal::Mammal" ] = *mammal;

This results in:

error: no match for ‘operator*’ (operand type is ‘Php::Class<Animal::Mammal>’)

I've tried numerous other things as well, but nothing seem to allow me to put whatever the template Php::Class creates into the map. I'm pulling my hair out, at this point. What am I missing?

Is it perhaps that the template Php::Class creates something that simply cannot be put in a map? How do I find out what the thing is that the template Php::Class actually creates?


Original question:

Although I have some cursory knowledge of C++, I'm new to actually programming with it.

I'm trying to develop a PHP extension with PHP-CPP. With this library you're able to define C++ classes and export them as PHP classes. Their documentation almost exclusively shows examples where you export those classes and methods inside it's main get_module() function.

However, as this could get rather cluttered pretty quickly, I'd like to move this logic close to the individual classes instead. So, I've created a template function for this:

namespace Animal
{
  template<typename T>
  void addToExtension( Php::Extension &extension, Php::Namespace &ns, ClassMap &map ) { }
}

Which I then specialize with something like:

namespace Animal
{
  class Mammal : public Php::Base
  {
  };

  template<>
  inline void addToExtension<Mammal>( Php::Extension &extension, Php::Namespace &ns, ClassMap &map ) {
    Php::Class<Mammal> clss( "Mammal" );

    // this is where I'm having extreme difficulty with
    map[ "Animal::Mammal" ] = clss;

    // add class to namespace
    ns.add( std::move( clss ) );
  }
}

...and in get_module() I then do this:

PHPCPP_EXPORT void *get_module() {
  static Php::Extension extension( "animals", "0.1" );

  Php::Namespace ns( "Animal" );

  Animal::ClassMap classMap;

  Animal::addToExtension<Animal::Mammal>( extension, ns, classMap );
  Animal::addToExtension<Animal::Dog>( extension, ns, classMap );
  Animal::addToExtension<Animal::Cat>( extension, ns, classMap );
etc...

  extension.add( std::move( ns ) );

  return extension;
}

As you can see, I want to add each individual class to the Animal::ClassMap map as well, because I want to use these when subsequent classes need to extend from them (for the export to PHP, that is):

// for instance, something like (not sure yet, if this is actually possible)
clss.extends( map[ "Animal::Mammal" ] );

But this is what I'm having extreme difficulties with. I've tried to define Animal::ClassMap in all sorts of manners:

namespace Animal
{
  // some examples of what I've tried:
  typedef std::map<std::string, Php::Class> ClassMap;
  typedef std::map<std::string, Php::Class*> ClassMap;
  typedef std::map<std::string, Php::Base> ClassMap;
  typedef std::map<std::string, Php::Base*> ClassMap;
  typedef std::map<std::string, Php::ClassBase> ClassMap;
  typedef std::map<std::string, Php::ClassBase*> ClassMap;
  typedef std::map<std::string, Php::Class<Php::Base>> ClassMap;
  typedef std::map<std::string, Php::Class<Php::Base>*> ClassMap;
  typedef std::map<std::string, std::shared_ptr<Php::Class>> ClassMap;
  // etc., etc., etc. ...
}

and tried to assign to it in all sorts of manners as well:

Php::Class<Mammal> clss( "Mammal" );

// some examples of what I've tried:
map[ "Animal::Mammal" ] = clss;
map[ "Animal::Mammal" ] = *clss;
map[ "Animal::Mammal" ] = &clss;
map[ "Animal::Mammal" ] = std::make_shared<Php::Class>( clss );
map[ "Animal::Mammal" ] = std::make_shared<Php::ClassBase>( clss );

map.insert( std::pair<std::string, Php::Class>( "Animal::Mammal", clss ) );
// etc., etc., etc. ...

but nothing compiles.

I've gotten compile errors ranging from "operator* not defined", to "incomplete class" (or something to that effect), to "template argument is invalid".

As you can see, I clearly lack an actual understanding of what needs to be done here.

How do I determine what the proper value type for the map should be and how do I then properly add a "class" (is it even a proper class?) to the map?


PS: I'm fairly confident I've correctly included all necessary headers/files in the appropriate files. I've just omitted those in my examples for brevity.

2

There are 2 answers

9
Caleth On

It looks like Php::Class is a template, so you can't have it as the mapped_type of a std::map, it isn't a type.

You are going to have to have a "register me" function for each class you are registering, that goes into a collection.

void registerMammal(Php::Namespace & ns) override 
{ 
    Php::Class<Animal::Mammal> cls("Mammal");

    cls.method<&Animal::Mammal::method>("method");
    // ... 

    ns.add(std::move(cls));
}

std::vector<std::function<void(Php::Extension &, Php::Namespace &)>> AnimalRegistrations{ registerMammal, ... }; 

PHPCPP_EXPORT void *get_module() {
    static Php::Extension extension( "animals", "0.1" );

    Php::Namespace ns( "Animal" );

    for (auto & reg : AnimalRegistrations)
    {
        reg(ns);
    }

    extension.add( std::move( ns ) );

    return extension;
}

If you have loads of namespaces to register, you may end up with a std::map<std::string, std::vector<std::function<void(Php::Namespace &)>>>, matching names to that namespaces collection of registration functions.

You'd use that like

Php::Extension extension( "dabbler", "0.1" );

for (auto & pair : NamespaceMap)
{
    Php::Namespace ns(pair.first);
    for (auto & reg : pair.second)
    {
        reg(ns);
    }
    extension.add( std::move( ns ) );
}
17
Decent Dabbler On

Okay, after lots of trial and error, I've finally managed to put a concrete result of the template Php::Class into a map and retrieve it as well:

std::map<std::string, Php::Class<Animal::Mammal>> classMap;

Php::Class<Animal::Mammal> mammal( "Mammal" );

classMap.insert( std::make_pair( "Animal::Mammal", mammal ) );

Php::Class<Animal::Mammal> mammal = classMap.at( "Animal::Mammal" );

I still don't understand why putting in into the map with:

classMap[ "Animal::Mammal" ] = mammal;

and retrieving it again with:

Php::Class<Animal::Mammal> mammal = classMap[ "Animal::Mammal" ];

wouldn't work, though. If someone could enlighten me about these intricacies, I'd appreciate it.

I presume it has something to do with the error messages I've shown in my question, about operators not being defined. I would have expected there to be some default behavior though, if operators are not explicitly defined, but it appears this is not the case.