Как мне вызвать все функции из подклассов, когда они были определены как супер-виртуальные в суперклассе? - PullRequest
1 голос
/ 18 мая 2011

Основной вопрос заключается в том, как мне реализовать startTest (), чтобы он вызывал runTest во всех подклассах. Спасибо!

/*******************
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();
  } 

Ответы [ 6 ]

3 голосов
/ 18 мая 2011

Ну, во-первых - принцип единоличной ответственности .Это означает, что ваш archeTest не должен управлять всеми тестовыми объектами.Просто сделайте (в) знаменитом Manager это!

#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];
  }
};

Запустите с

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

Опять же, смотрите комментарии в реализации TestManager.


Теперь, если вам действительно не нужен дополнительный класс ... это на самом деле проще, но это своего рода запах кода.Просто добавьте ваш класс в конструкторе archeTest в статический связанный список - проблема решена!Удалите его на уничтожение снова, конечно.Это работает, потому что каждый производный класс xxxstructor автоматически вызывает версию базового класса - конструктор * con * перед своим собственным и конструктор * de * после своего собственного:

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

Запустите его с помощью простого

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

, поскольку это статическая функция-член, ему не нужен экземпляр.;

Обратите внимание, что я использовал связанный список, поскольку это позволяетбезопасное удаление теста в середине списка без аннулирования ссылок, хранящихся в других объектах теста.

3 голосов
/ 18 мая 2011

Это возможно, но для этого вам придется использовать абстрактный шаблон фабрики. Пожалуйста, прочтите это , чтобы узнать, что такое абстрактный шаблон и как он может делать то, что вам нужно.

Если вы можете использовать boost в своем проекте, вы можете реализовать собственную абстрактную фабрику, используя шаблон boost :: factory .

Существует множество других реализаций абстрактной фабрики, которые вы могли бы использовать, если не хотите внедрять свои собственные. Здесь является ссылкой на одну такую ​​реализацию.

РЕДАКТИРОВАТЬ: В этом случае вам также понадобится некоторый механизм для регистрации новых тестовых случаев на заводе во время компиляции. Это может быть достигнуто путем использования препроцессора c ++ или шаблонов. Здесь - это подход, использующий шаблоны.

2 голосов
/ 18 мая 2011

Поскольку многим людям было трудно проследить все сообщения, которые я перечислил.Я реализовал версию этого с помощью Boost.

Хитрость заключается в том, что когда определение для TestTemplate расширяется определениями производного класса (новые тестовые случаи), оно вызывает вызов метода TestManager :: Register из-за инициализации статического const.

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

Это гарантирует, что функтор конструктора производного класса будет сохранен в карте TestManager.Теперь в TestManager я просто перебираю карту и использую функтор для создания экземпляров каждого Testcase и вызываю метод run для вновь созданного экземпляра.

Любой класс, производный от TestTemplate, будет зарегистрирован автоматически, нет необходимостичтобы вручную перечислить их все.

#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();
}

Я тестировал решение на VS05 и VS10.Ниже приведен ожидаемый результат.

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

Надеюсь, это прояснит ситуацию.

1 голос
/ 18 мая 2011

Возможно, вы захотите проверить UnitTest ++ (вы можете просмотреть исходный код здесь ). Обратите особое внимание на TestMacros.h и CheckMacros.h, которые, как следует из их названий, реализуют макросы для автоматического сбора и запуска тестов.

Следующий код, например, является «минимальной программой на C ++ для запуска неудачного теста через UnitTest ++.»

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

TEST(FailSpectacularly)
{
    CHECK(false);
}

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

Подробнее читайте в кратком обзоре UnitTest ++ . Я не углублялся в детали того, как реализованы макросы TEST и CHECK, но они позволяют объявлять тесты во многих различных файлах CPP, а затем запускать их все с помощью одного вызова UnitTest::RunAllTests(). Возможно, это достаточно близко к тому, что вы хотите сделать?

1 голос
/ 18 мая 2011

Во-первых, вы должны объявить startTest как это в archeTest.

virtual void startTest() = 0;

Это делает ее чисто виртуальной функцией, которая должна быть реализована в дочерних классах. Чтобы вызвать этот метод в дочернем классе, необходимо создать объект этого конкретного класса. Затем вы можете вызвать startTest либо через указатель базового класса, либо через указатель на дочерний класс. Обратите внимание, что в любом случае указатель должен указывать на экземпляр (конкретный объект) дочернего класса.

0 голосов
/ 18 мая 2011

РЕДАКТИРОВАТЬ: не важно, мой первый ответ не имеет никакого смысла.В C ++ вы не можете получить список всех подклассов класса, чтобы создать их экземпляр, поэтому я не думаю, что вы можете реализовать runTests () так, как вы этого хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...