C++: Warum wird diese Methode nicht aufgerufen?

Gast #3730659
Lesenswert?

Hallo,

ich versuche gerade mir eine Threadklasse für einen STM32 unter CooCox 
zu schreiben. Diese sieht folgendermaßen aus:
1
class Thread {
2
private:
3
  OS_STK task_stk[STACK_SIZE_TASK];
4
protected:
5
  static void taskmeth(void *arg);
6
public:
7
  virtual void Execute(void);
8
  void Init();
9
};
10

11
void Thread::taskmeth(void *arg)
12
{
13
  Thread *obj = (Thread *) arg;
14

15
  obj->Execute();
16
}
17

18
void Thread::Init()
19
{
20
  CoCreateTask((FUNCPtr) taskmeth, this, 0, &task_stk[STACK_SIZE_TASK - 1], STACK_SIZE_TASK);
21
}
22

23
void Thread::Execute(void)
24
{
25
  USART_SendData(USART2, 'W');
26
  char c = 'A';
27
  while (true) {
28
    USART_SendData(USART2, c++);
29
    if (c > 'Z')
30
      c = 'A';
31
    CoTickDelay(100);
32
  }
33
}

Problem: die Methode Execute wird nie erreicht.

Wenn ich Init aufrufe wird wie erwartet taskmeth aufgerufen. Ich sehe 
per Debugger dass "arg" die gleiche Adresse hatte wie vorher "this" in 
Init, die Übergabe scheint also richtig zu funktionieren. Eine sehr 
ähnliche Implementierung unter einem µC mit posixthreads funktioniert. 
Was könnte verkehrt sein?

LG
Stefan
#3730680
Lesenswert?

Stefan schrieb:
> void Thread::taskmeth(void *arg)
> {
>   Thread *obj = (Thread *) arg;
>
>   obj->Execute();
> }

ich schieße mal ins Blaue weil ich CoOS nicht kenne. Wenn das eine 
C-Implementierung ist, dann sollte die thread-Function auch extern "C" 
deklariert werden:

extern "C"
{
  void taskmeth(void *arg)
  {
    Thread *obj = (Thread *) arg;

    obj->Execute();
  }
}

die statische Funktion aus der Thread-Klasse muss dann raus. Aber wie 
gesagt, nur ein Verdacht.
Gast #3730713
Lesenswert?

Ja CoOs ist eine C Implementierung; aber die extern "C" Geschichte hat 
leider auch nicht geholfen. taskmeth() wird ja angesprungen.

@Florian
arg ist ein Zeiger auf ein Objekt der Klasse Thread, genau wie this. 
Dieser wird als Zeiger auf Thread gecastet und in obj gespeichert. Dann 
wird von obj mit dem Dereferenzierungsoperator -> die Methode 
aufgerufen... so meine Denke... Falsch?
Gast #3730716
Lesenswert?

CoCreateTask wird vom Framework zur Verfügung gestellt:

#define CoCreateTask(task,argv,prio,stk,stkSz)              \
            CreateTask(task,argv,(prio)|(((stkSz)<<8) &0x000FFF00 ),stk)
extern OS_TID      CreateTask(FUNCPtr task,void *argv,U32 
parameter,OS_STK *stk);
Gast #3730730
Lesenswert?

9          obj->Execute();
080014de:   ldr r3, [r7, #12]
080014e0:   ldr r3, [r3, #0]
080014e2:   ldr r3, [r3, #0]
080014e4:   ldr r2, [r7, #12]
080014e6:   adds r0, r2, #0
080014e8:   blx r3
10        }
080014ea:   mov sp, r7
080014ec:   add sp, #16
080014ee:   pop {r7, pc}
080014f0:   add r0, r0
080014f2:   ands r0, r0


Bis zum blx r3 kann ich steppen, dann schepperts ins Nirvana
Gast #3730746
Lesenswert?

Ja, ist so in den Beispielen. Das mit dem Stackende übergeben habe ich 
schon öfters auch in anderen Betriebssystemen gesehen, habe aber mal 
ausprobiert den Anfang zu übergeben, dann schepperts gleich. Habe mal 
zum Testen die while schleife direkt nach taskmeth kopiert, da drin 
gehts, nur der obj->Execute() Aufruf spackt :-(
#3730758
Lesenswert?

Stefan schrieb:
> Bis zum blx r3 kann ich steppen, dann schepperts ins Nirvana

Und in r3 steht auch die Adresse von Thread::Execute?

Auch wenn es hier nichts hilft. Die Threadfunktion muss eine 
"C"-Funktion sein. Wenn du das nicht beachtest wirst du noch so manches 
Wunder bei gemischter C C++ Programmierung erleben. qsort und Konsorten 
sind auch ein gutes Beispiel dafür.
Gast #3730776
Lesenswert?

Naja, zumindest muss es eine statische Methode sein, normale Methoden 
werden wohl anders behandelt... Ich habe noch was beim 
"Ursachenminimieren" herausgefunden:
1
void Thread::Init()
2
{
3
Thread *obj = this;
4
obj->Execute();

Selbst hier funktioniert der Aufruf schon nicht :-(
Gast #3730781
Lesenswert?

...und noch was: Selbst einfach nur "Execute();", ganz ohne 
Objektzeiger, geht nicht! ...wenn ich das "virtual" entferne gehts! Das 
virtual brauche ich später natürlich für die Polymorphie wenn ich 
Klassen von Thread ableite...
#3730791
Lesenswert?

Stefan schrieb:
> Naja, zumindest muss es eine statische Methode sein, normale Methoden
> werden wohl anders behandelt...

Das hat damit nichts zu tun. Selbst eine einfache Funktion in einer 
cpp-Datei ist was anders als in einer c-Datei. Die Parameter werden 
anders gehändelt.

Stefan schrieb:
> void Thread::Init()
> {
> Thread *obj = this;
> obj->Execute();

Wenn das geht:

void Thread::Init()
{
  Execute();
  ...

dann suchen wir an der falschen Stelle. Gibt es denn von Thread eine 
ordentliche Instanz?
#3730846
Lesenswert?

Ich tippe mal das liegt an deinem Startup-Code. Wird da die Funktion:

  __libc_init_array();

gerufen? Die ist eigentlich dafür da die Tabellen, die der Compiler eben 
auch für virtuelle Funktionen braucht, zu initialisieren. Dafür müssen 
auch Sektionen im Linker-Script angelegt werden. Macht das CooCox schon 
automatisch? Als ich vor ca. 2 Jahren das letzte mal CooCox probiert 
habe musste man das noch von Hand machen!
So etwa sieht das bei mir aus:

    /* C++ constructors etc */
    . = ALIGN(4);
    KEEP(*(.init))

    . = ALIGN(4);
    __preinit_array_start = .;
    KEEP (*(.preinit_array))
    __preinit_array_end = .;

    . = ALIGN(4);
    __init_array_start = .;
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array))
    __init_array_end = .;

    KEEP(*(.fini));

    . = ALIGN(0x4);
    KEEP (*crtbegin.o(.ctors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*crtend.o(.ctors))

    . = ALIGN(0x4);
    KEEP (*crtbegin.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*crtend.o(.dtors))
    /* End C++ */
    } > Flash

  /*
   * for exception handling/unwind - some Newlib functions (in common
   * with C++ and STDC++) use this.
   * Use KEEP so not discarded with --gc-sections
   */
  .ARM.extab : ALIGN(4)
    {
    KEEP(*(.ARM.extab* .gnu.linkonce.armextab.*))
    } > Flash
  __exidx_start = .;

  .ARM.exidx : ALIGN(4)
    {
    KEEP(*(.ARM.exidx* .gnu.linkonce.armexidx.*))
    } > Flash
  __exidx_end = .;

  _etext = .;


  /* MAIN DATA SECTION */
Gast #3730920
Lesenswert?

Oh ja, das scheint der richtige Fingerzeig zu sein!

https://www.google.de/url?sa=t&source=web&rct=j&ei=ZYPJU7CMEuem4gS_iYDYCA&url=http://www.coocox.org/forum/images/ckeditorfiles/20131023205634_How_to_use_CPlusPlus_in_CoIDE.pdf&cd=2&ved=0CCIQFjAB&usg=AFQjCNHMghXc4C1H2raFIJ6njkAZuNwrSA

Ist im Nachhinein betrachtet auch logisch; für virtuelle Methoden 
brauchts eine VMT für das dynamische Binden, und die wird nicht 
standardmäßig erzeugt.

Also vielen Dank!
Gast #3736131
Lesenswert?

So, das Linkerscript ist modifiziert, allerdings ist der startupcode bei 
mir dummerweise in Assembler geschrieben, und die verlinkte Anleitung 
einen Beitrag weiter oben geht von C aus. Ich weiß nicht wie ich das 
__libc_init_array(); unterbringen kann, das sehe ich auch in der 
Anleitung nicht :-( wie kommt die eigentlich her?
Gast #3736699
Lesenswert?

wenn du nicht weiter weißt, ruf sie doch in der main auf:
1
extern "C"
2
{
3
  void __libc_init_array(void); 
4
}
5

6
int main (void)
7
{
8
  __libc_init_array();
9
  ...
10
  ...

Die Funktion selbst sollte in einer der C++ Libs oder Objects vom 
Compiler liegen. Die sind bei mir im Linkerscript so drin (bezieht sich 
auf die gnu-arm-tools) :

GROUP 
(
libgcc.a
libc.a
libm.a
crti.o
crtn.o
crtbegin.o
crtend.o
)
Gast #3742819
Lesenswert?

Ok, in main aufrufen hat geklappt, vielen Dank!

Habe dann noch etwas nach Startupcode gegoogelt, der sieht nun so aus:

/* Call the clock system intitialization function.*/
    bl  SystemInit
    bl __libc_init_array

/* Call the application's entry point.*/
  bl main


Klappt! Virtuelle Methoden funktionieren, alles gut.

Danke & Gruß
Stefan

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren