VSCode + STM32 VS Code Extension環境でC++を利用する

STM32マイコンの開発環境をSTM32CubeIDEからVSCodeへ移行した。小規模なコントローラーを作るときはC言語のみで作っていたが、F4シリーズを使ったり、別のプロジェクトから移植するときにクラスが使えず・・・。ToolchainにはC++のコンパイラが入っているのになぜC++がコンパイルできないんだ?ということで、なんとかしてみたい。

環境前提

ソフトウェアバージョン
VSCode1.97.2
STM32 VS Code Extention2.1.1
STM32CubeMX6.13.0
CMake(VSCode Extention)0.0.17

プロジェクト構成

VSCodeでSTM32プロジェクトを構築する際は、以下の順序でツールをいじってテンプレートを作成する。

STEP
VSCodeを起動、STM32 VS Code Extentionを開き、STM32CubeMXを起動する
STEP
New Projectから該当MCUやボードを選択
STEP
Project名やProjectフォルダを指定、コード生成する
STEP
生成したProjectをSTM32 VS Code Extentionから読み込む

CMakeLists.txtを編集、C++に対応させる

CMakeがプロジェクトファイルを認識し、コンパイル指示を出している。それを設定するCMakeList.txtがプロジェクト内に出来上がっている。これを以下のように編集することでCコンパイラとC++コンパイラをファイル拡張子で切り替えつつ呼び出すことができる。

変更前:CMakeLists.txt

変更前のCMakeLists.txtを開く
01
02
03
04
05
06
07
08
09
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
cmake_minimum_required(VERSION 3.22)
 
#
# This file is generated only once,
# and is not re-generated if converter is called multiple times.
#
# User is free to modify the file as much as necessary
#
 
# Setup compiler settings
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
 
 
# Define the build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Debug")
endif()
 
# Set the project name
set(CMAKE_PROJECT_NAME VFD_NTP_Clock)
 
# Include toolchain file
include("cmake/gcc-arm-none-eabi.cmake")
 
# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
 
# Enable CMake support for ASM and C languages
enable_language(C ASM)
 
# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})
 
# Create an executable object type
add_executable(${CMAKE_PROJECT_NAME})
 
# Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx)
 
# Link directories setup
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined library search paths
)
 
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user sources here
)
 
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined include paths
)
 
# Add project symbols (macros)
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined symbols
)
 
# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
    stm32cubemx
 
    # Add user defined libraries
)

変更後:CmakeLists.txt

変更後のCmakeLists.txtを開く
01
02
03
04
05
06
07
08
09
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
cmake_minimum_required(VERSION 3.22)
 
#
# This file is generated only once,
# and is not re-generated if converter is called multiple times.
#
# User is free to modify the file as much as necessary
#
 
# Setup compiler settings
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_CXX_STANDARD 17)  # C++17を使用
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
 
# Define the build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Debug")
endif()
 
# Set the project name
set(CMAKE_PROJECT_NAME VFD_NTP_Clock)
 
# Include toolchain file
include("cmake/gcc-arm-none-eabi.cmake")
 
# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
 
# Enable CMake support for ASM and C languages
enable_language(C CXX ASM)
 
# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})
 
# C++ コンパイルオプション(STM32向け最適化)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-exceptions -fno-rtti -fno-threadsafe-statics")
 
# Create an executable object type
add_executable(${CMAKE_PROJECT_NAME})
 
# Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx)
 
# Link directories setup
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined library search paths
)
 
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user sources here
    Core/Src/test.cpp
)
 
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined include paths
)
 
# Add project symbols (macros)
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined symbols
)
 
# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
    stm32cubemx
 
    # Add user defined libraries
)

変更のポイント

C++のバージョン情報を指定する

1
2
3
set(CMAKE_CXX_STANDARD 17)  # C++17を使用
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

対応言語にCXXを追加する

1
2
# Enable CMake support for ASM and C languages
enable_language(C CXX ASM)

target_sourcesに.cppファイルを追加
.cppはプロジェクトに追加しただけではC++コンパイラでコンパイルされないので、ここだけは手動対応が必要。
.cファイルは自動で対応される。

1
2
3
4
# ソースファイルを追加(.cppファイルをここに追加)
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    Core/Src/test.cpp  # C++ ファイルを追加
)

CMAKE_CXX_FLAGSでコンパイルオプションを追加する

  • -fno-exceptions:C++ の例外 (try-catch) を無効化(STM32 では通常使わない)。
  • -fno-rtti:RTTI(Run-Time Type Identification)を無効化(通常は不要)。
  • -fno-threadsafe-statics:スレッドセーフな静的変数の初期化を無効化。
1
2
# C++ コンパイルオプション(STM32向け最適化)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-exceptions -fno-rtti -fno-threadsafe-statics")

これでC++ファイル(.cpp / .hpp)がコンパイルできるようになる。

ヘッダ・ソースファイルのC/C++対応

プロジェクトすべてをCやC++どちらかに統一できるのであれば、以下はあまり問題にならないが、STM32CubeMXの吐き出すコードはCだが、自作ライブラリはC++ということもあるだろう(今回のケースもこれ)。
そうしたとき、CからC++を呼び出すときにクラス定義などC++特有のコードを識別できずコンパイルエラーになってしまう。その解決方法を記す。

ヘッダファイル

C言語と同様にインクルードガードを設定し、多重読み込みを防止する。

その後、C言語で使用する変数・関数定義を「C言語の定義である」ことを明示する。
C++言語用の定義は、C++コンパイラのみがコンパイルできる場所に記述する。

sample.hpp
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef __SAMPLE_HPP //インクルードガード
#define  __SAMPLE_HPP
#ifdef __cplusplus //GCCでコンパイルするときは__cplusplusが定義されている
extern "C" //この命令でこの内部定義はC言語のもとの分かる C言語コンパイラは認識できないので上のifdefで場合分けする
{
#endif
 
    void func_c(int);//C言語用関数定義
    evtern int c_var;//C言語用変数定義
     
#ifdef __cplusplus
}
#endif
 
#ifdef __cplusplus
class sampleClass
{
public:
    void func_cpp();
    int cpp_var;
private:
    void func_cpp_pv();
    int cpp_ver_pv;
};
 
#endif //__SAMPLE_HPP

ソースファイル

.cppのソースファイルはC++コンパイラしか読み込み・コンパイルを行わないので、ヘッダファイルのような読み分け処理は不要。
しかし、C言語はC++言語のクラスを利用できないのでC++用関数をロードできない。例えばCの関数「func_c」の引数でCPPのクラスメンバ変数「cpp_var」の値を変更したい場合などは以下のようにする必要がある。

sample.cpp
1
2
3
4
5
6
7
8
9
#include "sample.hpp"
#include <new>
 
void func_c(int val)
{
    sampleClass *s = new sampleClass();//インスタンス生成
    s->cpp_var = 1;//メンバ変数の値を変更
    delete s;//メモリ解放
}

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!

この記事を書いた人

コメント

コメントする

This site uses Akismet to reduce spam. Learn how your comment data is processed.