Yves
Yves

Reputation: 12431

Is it possible to generate a compile-time error if some member function hasn't been called

I'm coding a class with Singleton pattern.

class GroupListDAO {
public:
    static GroupListDAO* getInstance() {
        static GroupListDAO groupListDAO;
        return &groupListDAO;
    }

    init(server::mysqldb::MysqlHelperTempalte* pHelper) {
        mysqlHT = pHelper;
    }

    bool getUserHeartNum(uint32_t owner, uint32_t& totalNum);
    bool setUserHeartNum(uint32_t owner, uint32_t totalNum, uint32_t update_time);
private:
    MysqlHelperTempalte *mysqlHT;

    GroupListDAO() = default;
    ~GroupListDAO() = default;
};

As you see, this class is used to connect to Mysql. So the member data mysqlHT must be initialized before calling any other member functions.

In a word, the class user must use this class as below:

GroupListDAO *p = GroupListDAO::getInstance();
p->init(XXX);    // init must be called before calling any other member functions
p->getUserHeartNum(...);
p->setUserHeartNum(...);

So I'm thinking if there is a way to force the class user to call the function init. Meaning that if the class user codes like this:

GroupListDAO *p = GroupListDAO::getInstance();
p->getUserHeartNum(...);
p->setUserHeartNum(...);

Some compile-time error can be generated.

Ofc, you might say that we can if (mysqlHT == nullptr) { throw exception; } in other member functions, but that would be runtime error, instead of compile-time error.

REAL CASE

One large project is developed by 5 developers. They are all using some Singleton objects. But they must say: Hey, I've initialized that, you guys can use it in your codes. Or, we are sorry, we all forget to initialize it...

Upvotes: 2

Views: 139

Answers (2)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123566

As suggested in a comment, you can use an argument with a default:

getInstance(server::mysqldb::MysqlHelperTempalte* pHelper = nullptr)

On the one hand it isnt nice to let the caller pass the parameter but using it only on the first call. On the other hand your design suggests that there is one place where you know it is the first call to getInstance (you don't want to init it twice, right?). Hence I propose this:

class GroupListDAO {
public:
    static GroupListDAO* createInstance(server::mysqldb::MysqlHelperTempalte* pHelper) {
        return getInstance(pHelper);
    }
    static GroupListDAO* getInstance() {
        return getInstance(nullptr);
    }
private:
    static GroupListDAO* getInstance(server::mysqldb::MysqlHelperTempalte* pHelper = nullptr) {
        static GroupListDAO groupListDAO(pHelper);
        return &groupListDAO;
    }
};

GroupListDAO perhaps needs to be a wrapper of the actual object and it can throw in its constructor when called with a nullptr. I am not aware of an easy way to create a compiler error.


PS: Actually I think you have conflicting requirements. On the one hand you want a singleton that hides initialization from the caller. The instance can be obtained via a getInstance without needing to care about the initialization. On the other hand you do want to care about initialization because the first call to getInstance is "special". Both, the singleton pattern, and using an init method, are not perfect idioms in isolation. When used together they are worse. I am not saying that it is an absolute no-go, but it isnt too surprising that a compromise has to be made when both approaches should be kept.

Upvotes: 3

MadScientist
MadScientist

Reputation: 3460

If you really want a compile error, you would have to get rid of your singleton pattern:

class GroupListDao
{
public:
  GroupListDao(...) { /* do your initialization here */ }

  bool getUserHeartNum(uint32_t owner, uint32_t& totalNum);
  bool setUserHeartNum(uint32_t owner, uint32_t totalNum, uint32_t update_time); 
  // ...
}

This way you ensure that any instance of GroupListDao has been initialized. You then would have to pass this instance around in your code. As consequence, you no longer can be sure that there is only one instance of GroupListDao in your application at compile time (if you really want you could track the number of created instances in your constructor and raise errors at runtime). Pick your poison ;)

Upvotes: 0

Related Questions