开发者

How do I call all functions from sub-classes when they were defined as pure virtual in the super-class?

开发者 https://www.devze.com 2023-03-06 10:30 出处:网络
The main question is how do I implement startTest() so that it calls runTest in all the subclasses.Thanks!

The main question is how do I implement startTest() so that it calls runTest in all the subclasses. Thanks!

/*******************
COMPILER TEST
*******************/

class archeTest
  {
  protected:
    short verbosity_;

  public:

    void setVerbosity(short v)
      {
      if( ((v == 1) || (v == 0) ) 
        {  
        verbosity_ = v;
        }
      else 
        {
        cout << " Verbosity Level Invalid " << endl;
        }
      }

    virtual void runTest() = 0;
      {
      }

    void startTest()
      {
      }
  };

class testNatives : public archeTest 
  {
  public:

    void runTest()
      {
      testInts<short>();
      testInts<int>();
      testInts<long>();
      testInts<unsigned short>();
      testInts<unsigned int>();
      testInts<unsigned long>();
      }      

    void reportResults() const
      {
      }

  protected:

    template<class T> void testFloats()

    template<class T> void testInts()
      {

      verbosity_ = 1;  

      T     failMax;
      short passState;
      short bitDepth;

      const char* a = typeid(T).name();
      bool signedType = ((*a == 't') || (*a == 'j') || (*a == 'm'));

      /* Bit Depth - Algorithm */

      T pow2 = 1, minValue = 0, maxValue = 0, bitCount = 0, failValue = 0;  
      while(pow2 > 0)
        {
        pow2 *= 2;
        maxValue = pow2-1;
 开发者_如何学运维       bitCount++;
        }
      failValue = pow2;

      int native1 = bitCount;
      int native2 = sizeof(T)*8;
      int native3 = numeric_limits<T>::digits;  
      if( !signedType )
        {
        native1++;
        native3++;
        }       
      if(verbosity_)
        {
        cout << endl << "**********\n" << reportType(a) << "\n**********" << endl << endl;
        cout << "Bit Depth - Algorithm:\t" << native1 << endl;
        cout << "Bit Depth - Sizeof:\t" << native2 << endl;
        cout << "Bit Depth - File:\t" << native3 << endl;
        }   
        if (native1 == native2 && native1 == native3)
          {
          cout << "Correlation:\t\tPass" << endl ;
          }
        else
          {
          cout << "Correlation:\t\tFail" << endl ;
          }
        cout << "Max Value:\t\t" << maxValue << endl;
        cout << "Max+1 Value:\t\t" << failValue << endl;
      } 

    string reportType(const char* c1)
      { 
      string s1;
      switch(*c1)
        {
        case 't':
          s1 = "Unsigned short";
          break;
        case 'j':
          s1 = "Unsigned int";
          break;
        case 'm':
          s1 = "Unsigned long";
          break;
        case 's':
          s1 = "Short";
          break;
        case 'i':
          s1 = "Int";
          break;
        case 'l':
          s1 = "Long";
          break;
        default:
          s1 = "Switch failed";
        }
      return s1;
      }
  };

int main()
  {
  testNatives A;
  A.runTest();
  } 


It is possible but to do it you will have to use an abstract factory pattern. Please read this for an overview on what the abstract pattern is and how it can do what you need.

If you can use boost in your project then you can implement your own abstract factory by using the boost::factory template.

There are a lot of other implementations for the abstract factory that you might be able to use if you do not want to roll out your own. Here is a link to one such implementation.

EDIT: In this case you will also need some mechanism to register new test cases with the factory at compile time. This can be achieved by leveraging either c++ preprocessor or templates. Here is an approach for this that uses templates.


Well, first - single responsibility principle. That in mind, your archeTest shouldn't manage alle test objects. Just have an (in)famous Manager do that!

#include <vector>

class TestManager{
  std::vector<archeTest*> _tests;
public:
  // either
  void AddTest(archeTest* test){
    _tests.push_back(test);
  }

  // OR
  archeTest* CreateTest(/*here_be_params*/){
    archeTest* test = new archeTest(/*params*/);
    // do whatever
    _tests.push_back(test);
    return test;        
  }

  void RunAllTests() const{
    for(int i=0; i < _tests.size(); ++i)
      _tests[i]->runTests(); 
  }

  // if you create tests in here, you also need to release them at the end
  // ONLY do this if your created the tests with CreateTest
  // or if you transfer the ownership of the test pointer to TestManager
  ~TestManager(){
    for(int i=0; i < _tests.size(); ++i)
      delete _tests[i];
  }
};

Run with

TestManager tmgr;
// create all your tests, either with
// archeTest* p = tmgr.CreateTest();
// OR
// archeTest* p = new archeTest();
// tmg.AddTest(p);
// and then run with
tmgr.RunAllTests();

Again, see the comments in the TestManager implementation.


Now, if you really really don't want an extra class ... that is actually easier, but it's kinda code smell. Just add your class in the constructor of archeTest into a static linked list - problem solved! Remove it on destruction again of course. This works because every derived class xxxstructor automatically calls the base class version - the *con*structor before its own and the *de*structor after its own:

#include <list>

class archeTest{
private:
  typedef std::list<archeTest*> TestList;
  static TestList _all_tests;

  // to erase the right test on destruction
  TestList::iterator _this_test;

public:
  archeTest(){
    _all_tests.push_back(this);
  }

  ~archeTest(){
    _all_tests.erase(_this_test);
  }

  static void RunAllTests(){
    for(TestList::iterator it = _all_tests.begin(); it != _all_tests.end(); ++it)
      (*it)->runTests();
  }
};

// in some TestManager.cpp
#include "TestManager.h"

TestManager::TestList TestManager::_all_tests;

Run it with a simple

// create all your tests;
// ...
archeTest::RunAllTests();

since it's a static member function, it doesn't need an instance.;

Note that I used a linked list, as that allows me to safely remove a test in the middle of the list without invalidating the references stored in the other test objects.


Since a lot of people were finding it difficult to follow through all the posts I listed. I implemented a version of this using boost.

The trick is the fact that when the definition for TestTemplate is expanded with the derived class definitions(new Test cases) it forces a call to the TestManager::Register method due to the initialization of the static const.

template<typename TestCase> 
const unsigned Test<TestCase>::m_uTestID = TestManager::Register(boost::factory<TestCase*>());

This ensures that a functor to the constructor of the derived class is stored in the TestManager's map. Now in the TestManager I simply iterate over the map and use the functor to instantiate each Testcase and call the run method on the newly created instance.

Any class that is derived from the TestTemplate will be registered automatically, No need to manually list all of them.

#include <map>
#include <iostream>
#include <boost/function.hpp>
#include <boost/functional/factory.hpp>

class ITest {
public:
    virtual void run() {
        runTest();
    }
    virtual void runTest() = 0;
};


typedef boost::function< ITest* ()> TestFactory;

class TestManager {
public:
    static int Register(TestFactory theFactory) {
    std::cout<<"Registering Test Case"<<std::endl;
        m_NumTests++;
        m_mapAllTests[m_NumTests] = theFactory;
        return m_NumTests;
    }

  void run() {
        for(unsigned uTestID = 1; uTestID <= m_NumTests; uTestID++) {
            ITest* theTestCase = m_mapAllTests[uTestID]();
            theTestCase->run();
            delete theTestCase;
        }
    }

private:
    static unsigned m_NumTests;
    static std::map<unsigned,  TestFactory> m_mapAllTests;
};

unsigned TestManager::m_NumTests = 0;
std::map<unsigned,  TestFactory> TestManager::m_mapAllTests;


template<typename TestCase>
class Test : public ITest {
public:
  unsigned getID() const {
    return m_uTestID;
  }
private:
    static const unsigned m_uTestID;
};
template<typename TestCase> 
const unsigned Test<TestCase>::m_uTestID = TestManager::Register(boost::factory<TestCase*>());



class Test1 : public Test<Test1> {
public:
    virtual void runTest() {
    std::cout<<"Test Id:"<<getID()<<std::endl;
    }
};

class Test2 : public Test<Test2> {
public:
    virtual void runTest() {
    std::cout<<"Test Id:"<<getID()<<std::endl;

    }
};


class Test3 : public Test<Test3> {
public:
    virtual void runTest() {
    std::cout<<"Test Id:"<<getID()<<std::endl;
    }
};

class Test4 : public Test<Test4> {
public:
    virtual void runTest() {
    std::cout<<"Test Id:"<<getID()<<std::endl;
    }
};

int main() {
    TestManager theManager;
    theManager.run();
}

I did test the solution on VS05 and VS10. Below is the expected output.

Registering Test Case
Registering Test Case
Registering Test Case
Registering Test Case
Test Id:1
Test Id:2
Test Id:3
Test Id:4

Hope it clears up some confusion.


First off, you would declare startTest like this in archeTest.

virtual void startTest() = 0;

That makes it a pure virtual function that must be implemented in child classes. To call this method on a child class, you must have an object of that particular class instantiated. Then you can call startTest either through a base class pointer or through a pointer to the child class. Note that in either case the pointer must be pointing to an instantiation (concrete object) of the child class.


You might want to check out UnitTest++ (you can browse the source code here). Pay particular attention to TestMacros.h and CheckMacros.h, which, as their names imply, implement macros to automatically collect and run tests.

The following code, for example, is "a minimal C++ program to run a failing test through UnitTest++."

// test.cpp
#include <UnitTest++.h>

TEST(FailSpectacularly)
{
    CHECK(false);
}

int main()
{
    return UnitTest::RunAllTests();
}

You can read more at the (brief) UnitTest++ overview. I haven't delved into the details of how the TEST and CHECK macros are implemented, but they do allow you to declare tests in many different CPP files and then run them all via a single call to UnitTest::RunAllTests(). Perhaps this is close enough to what you want to do?


EDIT: never mind, my first answer didn't make any sense. In C++, you cannot obtain the list of all subclasses of a class to instantiate them, so I don't think you can implement runTests() the way you want it.

0

精彩评论

暂无评论...
验证码 换一张
取 消