Merge branch 'feature/cxx_rtti_support' into 'master'
C++: allow enabling RTTI, add example Closes IDFGH-751 See merge request espressif/esp-idf!6342
This commit is contained in:
commit
1f323ca107
9 changed files with 229 additions and 3 deletions
5
Kconfig
5
Kconfig
|
@ -245,9 +245,8 @@ mainmenu "Espressif IoT Development Framework Configuration"
|
|||
memory for thrown exceptions when there is not enough memory on the heap.
|
||||
|
||||
config COMPILER_CXX_RTTI
|
||||
# Invisible option, until the toolchain with RTTI support is released.
|
||||
# Use prompt "Enable C++ run-time type info (RTTI)" when updating.
|
||||
bool
|
||||
bool "Enable C++ run-time type info (RTTI)"
|
||||
default n
|
||||
help
|
||||
Enabling this option compiles all C++ files with RTTI support enabled.
|
||||
This increases binary size (typically by tens of kB) but allows using
|
||||
|
|
6
examples/system/cpp_rtti/CMakeLists.txt
Normal file
6
examples/system/cpp_rtti/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(cpp_rtti)
|
9
examples/system/cpp_rtti/Makefile
Normal file
9
examples/system/cpp_rtti/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := cpp_rtti
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
69
examples/system/cpp_rtti/README.md
Normal file
69
examples/system/cpp_rtti/README.md
Normal file
|
@ -0,0 +1,69 @@
|
|||
# Example: C++ run-time type info (RTTI)
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates usage of the RTTI feature in ESP-IDF.
|
||||
|
||||
By default, RTTI support is disabled in ESP-IDF. It can be enabled using `CONFIG_COMPILER_CXX_RTTI` configuration option.
|
||||
|
||||
In this example, `sdkconfig.defaults` file sets `CONFIG_COMPILER_CXX_RTTI` option. This enables compile time support for RTTI (`-frtti` compiler flag).
|
||||
|
||||
The example prints demangled type names of a few objects and functions, obtained from `typeinfo().name`. The example also generates several objects of two classes, derived from a common base class. For each object, it is demonstrated that `dynamic_cast` behaves as expected, returning non-NULL when casting to the real object type, and NULL when casting to a different type.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Configure the project
|
||||
|
||||
To run this example, no additional configuration is necessary.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
Type names of a few objects:
|
||||
Type name of std::cout is: std::ostream
|
||||
Type name of std::cin is: std::istream
|
||||
Type of app_main is: void ()
|
||||
Type name of a lambda function is: app_main::{lambda(int, int)#1}
|
||||
|
||||
Generating 5 random objects and printing their types:
|
||||
obj->name() is: DerivedB
|
||||
typeid(*obj).name() is: DerivedB
|
||||
dynamic_cast<DerivedA*>(obj)=0
|
||||
dynamic_cast<DerivedB*>(obj)=0x3ffb7e88
|
||||
|
||||
obj->name() is: DerivedB
|
||||
typeid(*obj).name() is: DerivedB
|
||||
dynamic_cast<DerivedA*>(obj)=0
|
||||
dynamic_cast<DerivedB*>(obj)=0x3ffb7e9c
|
||||
|
||||
obj->name() is: DerivedA
|
||||
typeid(*obj).name() is: DerivedA
|
||||
dynamic_cast<DerivedA*>(obj)=0x3ffb7eb0
|
||||
dynamic_cast<DerivedB*>(obj)=0
|
||||
|
||||
obj->name() is: DerivedB
|
||||
typeid(*obj).name() is: DerivedB
|
||||
dynamic_cast<DerivedA*>(obj)=0
|
||||
dynamic_cast<DerivedB*>(obj)=0x3ffb7ec4
|
||||
|
||||
obj->name() is: DerivedA
|
||||
typeid(*obj).name() is: DerivedA
|
||||
dynamic_cast<DerivedA*>(obj)=0x3ffb7ed8
|
||||
dynamic_cast<DerivedB*>(obj)=0
|
||||
|
||||
Example finished.
|
||||
```
|
37
examples/system/cpp_rtti/example_test.py
Normal file
37
examples/system/cpp_rtti/example_test.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import IDF
|
||||
except ImportError:
|
||||
# this is a test case write with tiny-test-fw.
|
||||
# to run test cases outside tiny-test-fw,
|
||||
# we need to set environment variable `TEST_FW_PATH`,
|
||||
# then get and insert `TEST_FW_PATH` to sys path before import FW module
|
||||
test_fw_path = os.getenv('TEST_FW_PATH')
|
||||
if test_fw_path and test_fw_path not in sys.path:
|
||||
sys.path.insert(0, test_fw_path)
|
||||
import IDF
|
||||
|
||||
|
||||
@IDF.idf_example_test(env_tag='Example_WIFI')
|
||||
def test_real_time_stats_example(env, extra_data):
|
||||
dut = env.get_dut('cpp_rtti', 'examples/system/cpp_rtti')
|
||||
dut.start_app()
|
||||
|
||||
dut.expect('Type name of std::cout is: std::ostream')
|
||||
dut.expect('Type name of std::cin is: std::istream')
|
||||
dut.expect('Type of app_main is: void ()')
|
||||
dut.expect('Type name of a lambda function is: app_main::{lambda(int, int)#1}')
|
||||
|
||||
dut.expect('dynamic_cast<DerivedA*>(obj)=0')
|
||||
dut.expect('dynamic_cast<DerivedB*>(obj)=0x')
|
||||
dut.expect('dynamic_cast<DerivedB*>(obj)=0')
|
||||
dut.expect('dynamic_cast<DerivedA*>(obj)=0x')
|
||||
|
||||
dut.expect('Example finished.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_real_time_stats_example()
|
2
examples/system/cpp_rtti/main/CMakeLists.txt
Normal file
2
examples/system/cpp_rtti/main/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
idf_component_register(SRCS "rtti_example_main.cpp"
|
||||
INCLUDE_DIRS ".")
|
3
examples/system/cpp_rtti/main/component.mk
Normal file
3
examples/system/cpp_rtti/main/component.mk
Normal file
|
@ -0,0 +1,3 @@
|
|||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
100
examples/system/cpp_rtti/main/rtti_example_main.cpp
Normal file
100
examples/system/cpp_rtti/main/rtti_example_main.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
/* C++ run-time type info (RTTI) example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cxxabi.h>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::shared_ptr;
|
||||
using std::make_shared;
|
||||
|
||||
class Base;
|
||||
class DerivedA;
|
||||
class DerivedB;
|
||||
|
||||
static string demangle(const char* name);
|
||||
|
||||
class Base
|
||||
{
|
||||
public:
|
||||
virtual ~Base() {} ;
|
||||
virtual string name() = 0;
|
||||
static shared_ptr<Base> make_random_derived();
|
||||
};
|
||||
|
||||
class DerivedA : public Base
|
||||
{
|
||||
public:
|
||||
string name() override { return "DerivedA"; }
|
||||
};
|
||||
|
||||
class DerivedB : public Base
|
||||
{
|
||||
public:
|
||||
string name() override { return "DerivedB"; }
|
||||
};
|
||||
|
||||
/* Creates either DerivedA or DerivedB, depending on a random number */
|
||||
shared_ptr<Base> Base::make_random_derived()
|
||||
{
|
||||
if (std::rand() % 2 == 0) {
|
||||
return make_shared<DerivedA>();
|
||||
} else {
|
||||
return make_shared<DerivedB>();
|
||||
}
|
||||
}
|
||||
|
||||
/* Inside a .cpp file, app_main function must be declared with C linkage */
|
||||
extern "C" void app_main()
|
||||
{
|
||||
/* Demonstrate typeid().name() */
|
||||
cout << "Type names of a few objects:" << endl << '\t';
|
||||
cout << "Type name of std::cout is: " << demangle(typeid(std::cout).name()) << endl << '\t';
|
||||
cout << "Type name of std::cin is: " << demangle(typeid(std::cin).name()) << endl << '\t';
|
||||
cout << "Type of app_main is: " << demangle(typeid(app_main).name()) << endl << '\t';
|
||||
auto sum = [](int x, int y) -> int { return x + y; };
|
||||
cout << "Type name of a lambda function is: " << demangle(typeid(sum).name()) << endl << endl;
|
||||
|
||||
/* Demonstrate dynamic_cast */
|
||||
std::vector<shared_ptr<Base>> objects(5);
|
||||
cout << "Generating " << objects.size() << " random objects and printing their types:" << endl;
|
||||
std::generate(objects.begin(), objects.end(), Base::make_random_derived);
|
||||
for (auto &obj: objects) {
|
||||
cout << "obj->name() is: " << obj->name() << endl << '\t';
|
||||
cout << "typeid(*obj).name() is: " << demangle(typeid(*obj).name()) << endl << '\t';
|
||||
|
||||
const DerivedA* cast_to_derived_a = dynamic_cast<DerivedA*>(obj.get());
|
||||
const DerivedB* cast_to_derived_b = dynamic_cast<DerivedB*>(obj.get());
|
||||
|
||||
cout << "dynamic_cast<DerivedA*>(obj)=" << static_cast<const void*>(cast_to_derived_a) << endl << '\t';
|
||||
cout << "dynamic_cast<DerivedB*>(obj)=" << static_cast<const void*>(cast_to_derived_b) << endl << endl;
|
||||
}
|
||||
|
||||
cout << "Example finished." << endl;
|
||||
}
|
||||
|
||||
/* Helper function which converts typeid().name() to a human-readable type name */
|
||||
static std::string demangle(const char* name)
|
||||
{
|
||||
int status = 0;
|
||||
char* result = abi::__cxa_demangle(name, NULL, NULL, &status);
|
||||
string str_result;
|
||||
if (status == 0) {
|
||||
str_result = result;
|
||||
} else {
|
||||
str_result = name;
|
||||
}
|
||||
free(result);
|
||||
return str_result;
|
||||
}
|
1
examples/system/cpp_rtti/sdkconfig.defaults
Normal file
1
examples/system/cpp_rtti/sdkconfig.defaults
Normal file
|
@ -0,0 +1 @@
|
|||
CONFIG_COMPILER_CXX_RTTI=y
|
Loading…
Reference in a new issue