2

I'm trying to create a global map that will map strings to factory functions. This allows me to have a builder function read a key from an ini file, and then pass that ini file with its relevant section to the correct factory function. Here is the code in the header with the global map:

typedef Primitive* (*factory_func_t)(const ::INI&, const std::string&);

const std::map<std::string, factory_func_t> factory_funcs = {
    { SphereFactory::ID,     &SphereFactory::create_sphere },
    { QuadFactory::QUAD_ID,  &QuadFactory::create_quad },
    { QuadFactory::PQUAD_ID, &QuadFactory::create_pquad }
};

And here is an example of one of those factory classes:

class SphereFactory
{
  public:
    static const std::string ID;

    static Sphere* create_sphere(const ::INI&, const std::string& section);

    SphereFactory() = delete;

  private:
    static const std::string CENTER_KEY;
    static const std::string RADIUS_KEY;
};

const std::string SphereFactory::ID = "Sphere";

const std::string SphereFactory::CENTER_KEY = "Center";
const std::string SphereFactory::RADIUS_KEY = "Radius";

All this is giving me an error when compiling:

error: could not convert `{{cg::prim::factory::SphereFactory::ID,
 cg::prim::factory::SphereFactory::create_sphere}, 
 {cg::prim::factory::QuadFactory::QUAD_ID, 
 cg::prim::factory::QuadFactory::create_quad}, 
 {cg::prim::factory::QuadFactory::PQUAD_ID, 
 cg::prim::factory::QuadFactory::create_pquad}}'
 from `<brace-enclosed initializer list>'
 to `const std::map<std::basic_string<char>, cg::Primitive* (*)(const INI&, 
 const std::basic_string<char>&)>'

All of the above code is in the cg::prim::factory namespace, in case that makes a difference. Both Quad and Sphere inherit from Primitive. I'm using g++ -O3 -Wall -Wextra -pedantic -std=c++11 to compile.

Why is this not compiling?

1
  • I seem to have messed up with the formatting of the error message. I'm not sure how to make it wrap instead of displaying in one long line, sorry. Commented Jun 6, 2012 at 12:16

1 Answer 1

3

You are trying to use covariant return types: the typedef is for Primitive * but the factories return Sphere *, etc. You might include alternative, wrapper factory functions that return the base type.

As it is, C++ sees no relationship between the functions returning base and derived pointers. Only virtual functions are allowed to be "converted" that way, and factories can't be virtual. (Or at least, in C++ factory objects with virtual functions are discouraged. That would be typical in Java, but here a wrapper function would be better.)

Since initializer lists are still in their infancy, one way to debug (and work around potential compiler bugs that may still exist) is to manually cast things wherever possible:

const std::map<std::string, factory_func_t> factory_funcs = {
    { SphereFactory::ID,     (factory_func_t) &SphereFactory::create_sphere },
    { QuadFactory::QUAD_ID,  (factory_func_t) &QuadFactory::create_quad },
    { QuadFactory::PQUAD_ID, (factory_func_t) &QuadFactory::create_pquad }
};
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for your reply :) The problem was indeed the fact that it couldn't deduce Primary* from Sphere* or Quad*. I still find that odd, but as it stands my program works when my factories return the parent type. As for the casting to work around compiler bugs, it seems to work without that :) Perhaps they fixed the bug you encountered back in the days (you wrote about it before your edit)
@robrene Yes, it does appear to be fixed in GCC 4.7 release. But that was converting a lambda function to a std::function. Keeping it all to function pointers probably makes a difference :v) . By the way, don't forget to upvote if this was insightful and click the checkmark if it solved the problem.
static_cast please, this is C++ after all.
@RedX: For Real Code yes. Here I want to format nicely for the narrow StackOverflow code box, and it's only there to help the compiler along or attempt to clean up the error message (and then remove the cast when the error is fixed). A comment to that effect is essential. In my own code, I actually used temporary-construction notation with std::function. (code.google.com/p/c-plus/source/browse/src/phase_4.hpp line 43)
@vsaxena No, but I'd be interested to hear your ideas about how you'd like that to look and work. I envision something more like an interactive browser.
|

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.