自动注册新的派生类/创建者方法

时间:2015-08-30 14:48:50

标签: c++ templates macros

如果我举例说明我想要实现的目标,我认为这是最简单的。

让我们说我想实现一个单元测试环境,其中实现一个新的单元测试将涉及从给定的基类派生,并且(可能)遵循涉及添加其他宏的指导。然后,这样的新测试将自动添加到测试列表中,在某个时刻一个接一个地运行。然而有两件事:

  1. 我尝试尽可能快速轻松地创建每个新测试,特别是在使用测试本身修改文件以外的文件时。一个完美的情况就是这样,实施一个新的测试不需要触摸项目中的任何其他文件。这可以通过单例和可能的CRTP实现,但现在是第2点,

  2. 目标是具有有限RAM量的MCU(通常ROM不是问题),我希望能够直接在目标平台上运行测试。因此,在整个应用程序生命周期中占用内存的静态对象是不可接受的。相反,我希望能够在需要运行时单独创建和删除每个测试。

  3. 基本上,问题归结为一种自动将派生类型(或创建者方法)注册到具有最小RAM开销的工厂的方法(我假设会有一些,即至少指向所述方法的指针)。

    很抱歉没有代码示例,但如果没有提交给定的实现,那么这里没有什么可以显示的。

2 个答案:

答案 0 :(得分:1)

你能创建一个函数指针的静态/全局向量吗?这些将指向每个测试类的创建者/工厂函数。工厂函数返回指向基本测试类的指针。我打算尝试写出来,但我认为代码更容易编写和理解。

class TestBase
{
  public:
    static char registerTest(<function ptr type> creator) {
      testCreators.push_back(creator);
      return 1;
   }

    static void runTests()
    {
      for (auto creator : testCreators)
      {
        auto newTestClass = creator();
        newTestClass->tests();
        delete newTestClass;
      }
    }

  private:
    void tests() = 0;
    std::vector<function ptr type> testCreators;
};

然后是派生类。

class SpecificTest : public TestBase
{
  // Pretend test code is here.

  private:
  static char dummy;
};

// Plan old C function. Need to establish naming conventions so as
// not to get multiple symbol errors during linking. Kind of fragile.
TestBase* specificTestCreator()
{
  return new SpecificTest();
}

在SpecificTest的.cpp文件中

char SpecificTest::dummy = TestBase::registerTest(specificTestCreator);

我试图编译或运行它,但我认为它基本上是合理的。

答案 1 :(得分:1)

我根据Michael提供的答案创建了一个示例,编译并运行它。在下面发布代码。

TestBase.h:

#ifndef TESTBASE_H_
#define TESTBASE_H_

#include <vector>

class TestBase {
public:
    TestBase();
    virtual ~TestBase();
    static void RunAllTests();

protected:
    virtual void test() = 0;
    static char addTestCreator(TestBase* (*creator)());

private:
    static std::vector<TestBase* (*)()> &getTests();
};

#endif /* TESTBASE_H_ */

TestBase.cpp

#include "TestBase.h"

TestBase::TestBase() {
}

TestBase::~TestBase() {
}

char TestBase::addTestCreator(TestBase* (*creator)())
{
    getTests().push_back(creator);
    return 0;
}

void TestBase::RunAllTests()
{
    for(std::vector<TestBase* (*)()>::iterator it = getTests().begin(); it != getTests().end(); it++)
    {
        TestBase *t = (*it)();
        t->test();
        delete t;
    }
}

std::vector<TestBase* (*)()> &TestBase::getTests()
{
    static std::vector<TestBase* (*)()> v;
    return v;
}

ConcreteTest1.h:

#ifndef CONCRETETEST1_H_
#define CONCRETETEST1_H_

#include "TestBase.h"

class ConcreteTest1: public TestBase {
public:
    ConcreteTest1();
    virtual ~ConcreteTest1();

protected:
    void test();

private:
    // both here can be expanded with a macro to make it
    // easier as they'll be same for all derived classes
    static char dummy;
    static TestBase *creator();
};

#endif /* CONCRETETEST1_H_ */

ConcreteTest1.cpp:

#include "ConcreteTest1.h"
#include <iostream>

// can be expanded with a macro
char ConcreteTest1::dummy = TestBase::addTestCreator(ConcreteTest1::creator);
// can be expanded with a macro
TestBase* ConcreteTest1::creator()
{
    return new ConcreteTest1();
}

ConcreteTest1::ConcreteTest1()
{
    std::cout << "Creating test 1" << std::endl;
}

ConcreteTest1::~ConcreteTest1()
{
    std::cout << "Deleting test 1" << std::endl;
}

void ConcreteTest1::test()
{
    std::cout << "Running test 1" << std::endl;
}

类似ConcreteTest2.cpp / .h。

从main调用:

TestBase::RunAllTests();

输出是:

Creating test 1
Running test 1
Deleting test 1
Creating test 2
Running test 2
Deleting test 2

这正是我想要实现的目标。