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:
Ivan Grokhotkov 2019-11-07 01:00:32 +08:00
commit 1f323ca107
9 changed files with 229 additions and 3 deletions

View file

@ -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

View 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)

View 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

View 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.
```

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

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "rtti_example_main.cpp"
INCLUDE_DIRS ".")

View file

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View 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;
}

View file

@ -0,0 +1 @@
CONFIG_COMPILER_CXX_RTTI=y