I'm trying to create an easy way to register all my Project Euler solutions into a std::map in a factory pattern, in order to be able to refer to them in code by number. I found a fantastic answer on this site (Dynamically register constructor methods in an AbstractFactory at compile time using C++ templates) and came up with this solution:
EulerProblem.h:
#ifndef EULERPROBLEM_H
#define EULERPROBLEM_H
#include<string>
#include<sstream>
#include<QObject>
#include<QString>
// BASE PROBLEM CLASS
class EulerProblem : public QObject
{
Q_OBJECT
signals:
void printSignal(QString str);
void debugSignal(QString str);
void problemTextSignal(QString str);
protected:
EulerProblem() {}
void print(QString str);
void debug(QString str);
void setProblemText(QString str);
protected:
int problemNumber;
QString problemText;
public:
virtual ~EulerProblem() { }
void initialize();
virtual void doProblem() = 0;
};
// PROBLEM TEMPLATE, DERIVE PROBLEMS FROM THIS
template<int NUM, typename IMPL>
class ProblemTmpl : public EulerProblem
{
enum { _PROBLEM_ID = NUM };
public:
static EulerProblem* Create() { return new IMPL(); }
static const uint16_t PROBLEM_ID; // for registration
// static void Enable() { volatile uint16_t x = PROBLEM_ID; }
protected:
ProblemTmpl() { problemNumber = PROBLEM_ID; } //use parameter to instantiate template
};
// PROBLEM FACTORY, USE THIS TO GET PROBLEMS
class ProblemFactory
{
public:
typedef EulerProblem* (*t_pfFactory)();
static ProblemFactory *getInstance()
{
static ProblemFactory fact;
return &fact;
}
uint16_t Register(uint16_t msgid, t_pfFactory factoryMethod)
{
printf("Registering constructor for msg id %d\n", msgid);
m_List[msgid] = factoryMethod;
return msgid;
}
EulerProblem *Create(uint16_t msgid)
{
return m_List[msgid]();
}
std::map<uint16_t, t_pfFactory> m_List;
private:
ProblemFactory() {};
ProblemFactory(ProblemFactory const&) {};
ProblemFactory& operator=(ProblemFactory const&);
~ProblemFactory() {};
};
#endif // EULERPROBLEM_H
EulerProblem.cpp (note the first line, which is intended to automatically call Register()):
#include "eulerproblem.h"
template <int TYPE, typename IMPL>
const uint16_t ProblemTmpl<TYPE, IMPL>::PROBLEM_ID =
ProblemFactory::getInstance()->Register(ProblemTmpl<TYPE, IMPL>::_PROBLEM_ID, &ProblemTmpl<TYPE, IMPL>::Create);
void EulerProblem::initialize()
{
setProblemText(problemText);
}
void EulerProblem::debug(QString str)
{
emit debugSignal(str);
}
void EulerProblem::print(QString str)
{
emit printSignal(str);
}
void EulerProblem::setProblemText(QString str)
{
emit problemTextSignal(str);
}
Example problem class (049.h):
#ifndef _49_h
#define _49_h
class Problem049 : public ProblemTmpl<49, Problem049>
{
public:
Problem049()
{
problemText =
"The arithmetic sequence, 1487, 4817, 8147, in which each of the terms increases by 3330, is unusual in two ways: (i) each of the three terms are prime, and, (ii) each of the 4-digit numbers are permutations of one another.\n"
"\n"
"There are no arithmetic sequences made up of three 1-, 2-, or 3-digit primes, exhibiting this property, but there is one other 4-digit increasing sequence.\n"
"\n"
"What 12-digit number do you form by concatenating the three terms in this sequence?";
}
void doProblem()
{
// problem solution here
}
};
#endif /* _49_h */
So when I use the following code (the connect() calls are Qt stuff for wiring up signals):
ep = ProblemFactory::getInstance()->Create(49);
connect(ep, SIGNAL(printSignal(QString)), this, SLOT(addOutput(QString)));
connect(ep, SIGNAL(debugSignal(QString)), this, SLOT(addDebug(QString)));
connect(ep, SIGNAL(problemTextSignal(QString)), this, SLOT(setProblem(QString)));
ep->initialize();
I get a segfault from ProblemFactory::Create() because the std::map is empty--Register() was never called. The code compiles fine, however. Can anyone see what I'm doing wrong here? I've been hunting for a while.
I ran and compiled the example given as an answer in the other question, and it works fine. It's not something conflicting with Qt, is it?