[C++] Compile-time verzameling van functions

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 16:19

diondokter

Dum spiro, spero

Topicstarter
Hallo beste tweakers,

ik ben bezig om in C++ (arm-none-eabi-g++) voor microcontrollers een simpele unittest framework op te zetten. Nu wil ik alleen een manier hebben om alle test functions aan te kunnen roepen. Dit kan natuurlijk heel simpel zijn door elke functie gewoonweg aan te roepen in de main.

Het is dan alleen zo dat wanneer er tientallen van zijn, het veel ruimte in de code kost en er makkelijk een functie over het hoofd geslagen kan worden. Het handigste zou zijn als de compiler dat voor mij deed.

Wat ik in sommige frameworks zie is:
C++:
1
2
3
4
TEST_FUNCTION(myfunc)
{
    ASSERT(10 == 10);
}


Deze functions hoef je dan niet zelf 'aan te melden', maar zijn blijkbaar toch aan te roepen. Wanneer ik de macros en code ervan inspecteer kan ik niet ontdekken hoe dit werkt.
Ook vele google searches hebben mij weinig bruikbaars gebracht.

In C# zou dit erg makkelijk zijn met reflectie. :9~ Maar dat is natuurlijk niet haalbaar met maar 128kb ROM :P

Hopelijk kan iemand me het juiste pad aanwijzen.

Dion Dokter

Beste antwoord (via diondokter op 29-11-2017 08:19)


  • ozkilator
  • Registratie: Februari 2004
  • Niet online
Achter de "TEST_FUNCTION" macro schuilt een klasse waarvan direct een instantie aangemaakt wordt. Deze "meld" zich vervolgens bij het test framework.Tenminste zo doet het google testframework dit.

code:
1
2
3
4
5
6
7
class TEST_FUNCTION {
public:
  TEST_FUNCTION(){  
      //Register class
  }

} TEST_FUNCTION_instance;

Yoda: Very dark the other side is.. hmm.. too dark... Obi-Wan: Yoda, shut up and eat your toast allready...

Alle reacties


Acties:
  • 0 Henk 'm!

  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
diondokter schreef op dinsdag 28 november 2017 @ 11:36:
Hallo beste tweakers,

ik ben bezig om in C++ (arm-none-eabi-g++) voor microcontrollers een simpele unittest framework op te zetten. Nu wil ik alleen een manier hebben om alle test functions aan te kunnen roepen. Dit kan natuurlijk heel simpel zijn door elke functie gewoonweg aan te roepen in de main.

Het is dan alleen zo dat wanneer er tientallen van zijn, het veel ruimte in de code kost en er makkelijk een functie over het hoofd geslagen kan worden. Het handigste zou zijn als de compiler dat voor mij deed.

Wat ik in sommige frameworks zie is:
C++:
1
2
3
4
TEST_FUNCTION(myfunc)
{
    ASSERT(10 == 10);
}


Deze functions hoef je dan niet zelf 'aan te melden', maar zijn blijkbaar toch aan te roepen. Wanneer ik de macros en code ervan inspecteer kan ik niet ontdekken hoe dit werkt.
Ook vele google searches hebben mij weinig bruikbaars gebracht.

In C# zou dit erg makkelijk zijn met reflectie. :9~ Maar dat is natuurlijk niet haalbaar met maar 128kb ROM :P

Hopelijk kan iemand me het juiste pad aanwijzen.

Dion Dokter
Worden er pre-compilation steps uitgevoerd? Ik weet bijvoorbeeld dat de Unreal engine een precompilation step heeft die naar attributes zoekt op classes (wat macro's zijn die als tags/attributes fungeren) en dan een source file genereerd die allerlei plumbing implementeerd. Wellicht een vergelijkbare aanpak?

Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 16:19

diondokter

Dum spiro, spero

Topicstarter
Worden er pre-compilation steps uitgevoerd?
De setup qua compilen is nu nog zo goed als standaard. Er wordt dus nog niks uitgevoerd dat voor het compilen zelf niet nodig is.
Maar die kant op zou zeker wel kunnen.

Ik heb ook opgezocht hoe je (pre)compile-time code uitvoert, maar kon niks concreets vinden.
Is dit met macro's te doen?

Acties:
  • Beste antwoord
  • +1 Henk 'm!

  • ozkilator
  • Registratie: Februari 2004
  • Niet online
Achter de "TEST_FUNCTION" macro schuilt een klasse waarvan direct een instantie aangemaakt wordt. Deze "meld" zich vervolgens bij het test framework.Tenminste zo doet het google testframework dit.

code:
1
2
3
4
5
6
7
class TEST_FUNCTION {
public:
  TEST_FUNCTION(){  
      //Register class
  }

} TEST_FUNCTION_instance;

Yoda: Very dark the other side is.. hmm.. too dark... Obi-Wan: Yoda, shut up and eat your toast allready...


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 16:19

diondokter

Dum spiro, spero

Topicstarter
Och ja, je kunt meteen na de definitie een instance aanmaken van de klasse. Met die macro kun je dan natuurlijk ook de code meteen in de goede functie van de klasse zetten.
Top!

Ik denk dat ik er zo uit kom :)

Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Werkt niet betrouwbaar, overigens. Objecten hoeven niet aangemaakt te worden totdat ze noodzakelijk zijn, en deze objecten zijn dat nooit. Het gaat alleen goed als een compiler de instances toevallig nog voor main() aanmaakt.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 16:19

diondokter

Dum spiro, spero

Topicstarter
MSalters schreef op maandag 4 december 2017 @ 00:32:
Werkt niet betrouwbaar, overigens. Objecten hoeven niet aangemaakt te worden totdat ze noodzakelijk zijn, en deze objecten zijn dat nooit. Het gaat alleen goed als een compiler de instances toevallig nog voor main() aanmaakt.
Toch lijkt het wel te werken. Dit is de uiteindelijke code:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class UnitTest
{
    public:
    UnitTest();
    virtual void TestBody() = 0;
    virtual const char* GetTestName() = 0;
};

void RunTests();
void RegisterTest(UnitTest* Value);

#define TestFunction(Name)\
class UnitTest##Name : public UnitTest \
{\
    public:\
    UnitTest##Name() {}\
    \
    virtual void TestBody();\
    virtual const char* GetTestName();\
} UnitTest##Name##Instance; \
\
const char* UnitTest##Name::GetTestName() { return #Name; }\
\
void UnitTest##Name::TestBody()


void RegisterTest(UnitTest* Value)
{
    if(Tests == nullptr)
    {
        Tests = new lni::vector<UnitTest*>();
    }
    
    Tests->push_back(Value);
}

UnitTest::UnitTest()
{
    RegisterTest(this);
}


Van elke klasse wordt een global instance gemaakt die zichzelf aanmeldt met in zijn constructor.

Alle globals worden eerst geïnitialiseerd en pas daarna wordt de main aangeroepen, toch?
Of zijn er situaties waarbij dat niet zo is?

Acties:
  • +1 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Zoals ik al zei, het hangt van de compiler af. Soms werkt het, soms niet.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein

Pagina: 1