Stefano Sabatini
10/24/2008 11:29:00 AM
On 2008-10-24, Stefano Sabatini <stefano.sabatini@caos.org> wrote:
> On 2008-10-24, Stefano Sabatini <stefano.sabatini@caos.org> wrote:
>> Hi all, I'm encountering this while trying to implement a factory
>> singleton method to generate objects.
>>
>> The singleton has a static map which binds a static creation function
>> defined in each class to the type of the object to be created.
>>
>> Here it is the code, which is a modification of the wikipedia C++
>> factory example code:
>>
>> ----------------------------------8<--------------------------------
> [...]
>> ----------------------------------8<--------------------------------
>
> Sorry it had tons of erros, check below the new version.
>
>> The static map declaration syntax is somehow wrong, and after hitting
>> my head sometime I still can't get out of it.
>>
>> I'm using g++ 4.3.1, and the syntax error I get is this:
>>
>> make PizzaFactory2; and PizzaFactory2
>> g++ -I/home/stefano/opt/reilabs/include -I/home/stefano/include -O0 -g -ggdb PizzaFactory2.cxx -c -o PizzaFactory2.o
>> PizzaFactory2.cxx:50: error: `*' cannot appear in a constant-expression
>> PizzaFactory2.cxx:50: error: a function call cannot appear in a constant-expression
>> PizzaFactory2.cxx:50: error: `*' cannot appear in a constant-expression
>> PizzaFactory2.cxx:50: error: a function call cannot appear in a constant-expression
>> PizzaFactory2.cxx:50: error: a function call cannot appear in a constant-expression
>> PizzaFactory2.cxx:50: error: template argument 2 is invalid
>>
>> The exact line of the error is:
>> static std::map<std::string, (Pizza *)(*)()> creators;
>>
>> which I interpret as:
>> a static map from string to a static method pointer which takes no
>> parameters and returns a pointer to a Pizza object.
>>
>> What am I missing or what I'm doing wrongly?
>
> Well I found a solution, even if I'm not sure I really understood it.
>
> If I define the type like this:
> typedef Pizza *(* pizza_creator_fn_ptr)();
>
> and then use the map like this:
> static map<std::string, pizza_creator_fn_ptr> creators;
>
> then it seems to work fine.
>
> A short explanation would be nice.
As we discovered the problem was the superfluous parentheses.
> New version here:
> -----------------------------------8<-------------------------------------
[...]
> -----------------------------------8<-------------------------------------
>
> The code has still a problem related to the use of the static map
> which isn't found by the compiler, but this is another problem.
For the archive, the problem was due to the (non obvious) fact that a
static map has to be also defined in the implementation, that is
outside the delclaration. Adding the missing definition:
std::map<std::string, pizza_creator_ptr> PizzaFactory::creators;
fixed the problem.
Reposting the correct and running version of the toy pizza creator
singleton/factory sample.
----------------------------------------8<------------------------
#include <string>
#include <map>
#include <iostream>
class Pizza {
public:
virtual void get_price() = 0;
};
class HamAndMushroomPizza: public Pizza {
public:
virtual void get_price() {
std::cout << "Ham and Mushroom: $8.5" << std::endl;
}
static Pizza* create_pizza() {
return new HamAndMushroomPizza;
}
};
class DeluxePizza : public Pizza {
public:
virtual void get_price() {
std::cout << "Deluxe: $10.5" << std::endl;
}
static Pizza* create_pizza() {
return new DeluxePizza;
}
};
class SeafoodPizza : public Pizza {
public:
virtual void get_price() {
std::cout << "Seafood: $11.5" << std::endl;
}
static Pizza* create_pizza() {
return new SeafoodPizza;
}
};
typedef Pizza* (*pizza_creator_ptr)(void);
class PizzaFactory {
private:
static std::map<std::string, pizza_creator_ptr> creators;
void init() {
PizzaFactory::creators["Deluxe"] = &DeluxePizza::create_pizza;
PizzaFactory::creators["Ham and Mushroom"] = &HamAndMushroomPizza::create_pizza;
PizzaFactory::creators["Seafood"] = &SeafoodPizza::create_pizza;
}
public:
static PizzaFactory* get_instance()
{
static PizzaFactory * instance = 0;
if (!instance) {
instance = new PizzaFactory;
instance->init();
}
return instance;
}
Pizza* create_pizza(const std::string& type) {
std::map<std::string, pizza_creator_ptr>::iterator it;
if ((it = PizzaFactory::creators.find(type)) != creators.end())
return (*(it->second))();
else
return 0;
}
};
std::map<std::string, pizza_creator_ptr> PizzaFactory::creators;
//usage
int main() {
PizzaFactory* factory = PizzaFactory::get_instance();
Pizza *pizza = 0;
if (pizza = factory->create_pizza("Default")) {
pizza->get_price();
delete pizza;
}
if (pizza = factory->create_pizza("Ham and Mushroom")) {
pizza->get_price();
delete pizza;
}
if (pizza = factory->create_pizza("Seafood")) {
pizza->get_price();
delete pizza;
}
return 0;
}
----------------------------------------8<------------------------
Regards.