0

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?

Community
  • 1
  • 1
AJS
  • 3
  • 2
  • Doesn't Answer Two in the accepted answer in the link you gave solve that? – o_weisman Jan 24 '16 at 07:22
  • I don't think so--answer two deals with a segfault that occurs before main() is even reached. My problem is a segfault that occurs during execution because the std::map is empty due to the fact that Register() is never called. I can set a breakpoint before that ep=ProblemFactory::getInstance line and execution reaches it no problem. I put a breakpoint in Register() and execution never reaches it, even though it's supposed to reach it before main() is even called. – AJS Jan 24 '16 at 09:15
  • Why did you replace the original's use of the static member variable in the constructor with `Enable()`? – molbdnilo Jan 24 '16 at 09:29
  • I didn't replace it, i just added Enable() out of suspicion that since PROBLEM_ID wasn't being referenced the compiler would just eliminate it at compile time. Problem persists whether or not Enable() is there. I guess I don't need it as I seem to have left in both. – AJS Jan 24 '16 at 10:05
  • Or rather, I didn't leave in both but The problem persists using either method. I guess I'll use the original method of referencing it in the constructor, as that method has less nonsense in it. Thanks. – AJS Jan 24 '16 at 10:12

1 Answers1

0

Figured it out, though I don't understand why the solution worked.

I moved the following line:

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);

From the top of EulerProblem.cpp to the bottom of EulerProblem.h and it worked. Does anyone have any insight as to why that is?

AJS
  • 3
  • 2