Skip to content
Snippets Groups Projects
Commit 596f66fd authored by Alexander Mandera's avatar Alexander Mandera
Browse files

Fix error when using C++ virtual methods

parent b4d38371
No related branches found
No related tags found
No related merge requests found
.pio
.vscode
.idea
*.elf
*.hex
*.lst
......
......@@ -660,6 +660,10 @@ mini_pprintf(int (*puts)(char*s, int len, void* buf), void* buf, const char *fmt
Copyright 2023 Charles Lohr, under the MIT-x11 or NewBSD licenses, you choose.
*/
#ifdef CPLUSPLUS
// Method to call the C++ constructors
void __libc_init_array(void);
#endif
int main() __attribute__((used));
void SystemInit( void ) __attribute__((used));
......@@ -802,6 +806,12 @@ asm volatile(
bne a1, a2, 1b\n\
2:\n" );
#ifdef CPLUSPLUS
asm volatile(
"call %0 \n\t" // Call __libc_init_array function
: : "i" (__libc_init_array) : );
#endif
SETUP_SYSTICK_HCLK
// set mepc to be main as the root app.
......@@ -973,3 +983,29 @@ void DelaySysTick( uint32_t n )
while( ((int32_t)( SysTick->CNT - targend )) < 0 );
}
// C++ Support
#ifdef CPLUSPLUS
// This is required to allow pure virtual functions to be defined.
extern void __cxa_pure_virtual() { while (1); }
// These magic symbols are provided by the linker.
extern void (*__preinit_array_start[]) (void) __attribute__((weak));
extern void (*__preinit_array_end[]) (void) __attribute__((weak));
extern void (*__init_array_start[]) (void) __attribute__((weak));
extern void (*__init_array_end[]) (void) __attribute__((weak));
void __libc_init_array(void)
{
size_t count;
size_t i;
count = __preinit_array_end - __preinit_array_start;
for (i = 0; i < count; i++)
__preinit_array_start[i]();
count = __init_array_end - __init_array_start;
for (i = 0; i < count; i++)
__init_array_start[i]();
}
#endif
\ No newline at end of file
all : flash
TARGET:=cpp_virtual_methods
TARGET_EXT:=cpp
ADDITIONAL_C_FILES+=example.cpp
include ../../ch32v003fun/ch32v003fun.mk
# Removing compiler optimization to small file size
# because it optimizes the virtual functions out
# which are tested here.
CFLAGS:= \
-g -flto -ffunction-sections \
-static-libgcc \
-march=rv32ec \
-mabi=ilp32e \
-I/usr/include/newlib \
-I$(CH32V003FUN) \
-nostdlib \
-I. -Wall
CFLAGS+=-fno-rtti -DSTDOUT_UART -DCPLUSPLUS
flash : cv_flash
clean : cv_clean
\ No newline at end of file
# C++ Virtual Methods Example
This example tests the usage of classes with virtual methods in C++.
Without the usage of `__libc_init_array`, the virtual table of the class instance `Example` is not initialized correctly:
```
vtable for 'ExampleClass' @ 0x0 (subobject @ 0x20000004):
[0]: 0x11e0006f
[1]: 0x0 <InterruptVectorDefault()>
```
In this case, a call to a virtual method will result to an invalid call
and the program won't work as expected.
Here, the program will also print out `Begin example`, but does not print any values.
It seems like the MCU resets here.
In other environments like with the Arduino Core, the hard fault handler is called
and the program gets into an infinite loop.
With the macro `CPLUSPLUS`, an implemention auf `__libc_init_array` is called on startup.
Then the virtual table is initialized correctly and new values are printed as expected:
```
vtable for 'ExampleClass' @ 0x212c (subobject @ 0x20000004):
[0]: 0x1fb0 <ExampleClass::doNewLine()>
[1]: 0x1fda <ExampleClass::printValue(int)>
```
\ No newline at end of file
/*
* Example for using virtual methods in C++
* 05/21/2023 A. Mandera
*/
// Could be defined here, or in the processor defines.
#define SYSTEM_CORE_CLOCK 48000000
#define APB_CLOCK SYSTEM_CORE_CLOCK
#include "ch32v003fun.h"
#include "example.h"
#include <stdio.h>
int main() {
SystemInit48HSI();
// Setup UART @ 115200 baud
SetupUART(UART_BRR);
Delay_Ms(100);
printf("Begin example\n");
// Initialize variable in example class
Example.begin();
while (true) {
Example.doPrint(10);
Delay_Ms(1000);
}
}
\ No newline at end of file
/*
* Example for using virtual methods in C++
* 05/21/2023 A. Mandera
*/
#include "example.h"
#include "stdio.h"
ExampleClass Example;
void Print::doPrint(int i) {
printValue(i);
doNewLine();
}
void ExampleClass::begin() {
this->_value = 1;
}
int ExampleClass::value() {
return this->_value++;
}
void ExampleClass::doNewLine() {
printf("\n");
}
void ExampleClass::printValue(int i) {
printf("Value: %d", this->value() + i);
};
\ No newline at end of file
/*
* Example for using virtual methods in C++
* 05/21/2023 A. Mandera
*/
#pragma once
class Print {
public:
void doPrint(int);
virtual void doNewLine(void) = 0;
virtual void printValue(int) = 0;
};
class ExampleClass : public Print {
public:
void begin();
int value();
void doNewLine(void) override;
void printValue(int) override;
private:
int _value;
};
extern ExampleClass Example;
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment