Using C++ for a custom μPy module?

Hi! Loving the board so far and definitely planning to get more.

How hard would it be for a custom Micropython module (like src/omv/modules/examplemodule.c) to use an extern "C" symbol defined in an object file generated by a C++ compiler (arm-none-eabi-g++)? I don’t need heap allocations or exceptions or anything fancy like that, but it would be lovely if I could use a few searching/sorting/iteration templates from the STL.

It seems like the module definitions in src/omv/modules/ are all linked together at some point, but I don’t see any corresponding object files in the build/ directory and, looking through the Makefiles, “ld” isn’t a particularly easy keyword to search : )

You can compile things with g++. However, g++ loves to include lots of things you don’t want.

It’s quite a challenge to get rid of all the extra stuff that g++ will pull in.

Thanks for the quick response! I’m willing to at least give it a try and report back.

Where can I put the object file such that the linker will find it?

Hi, the linker is told to link object files in one of the MakeFiles. You need to edit our MakeFile to add the object.

OK, so after a long and harrowing journey through a majestic sea of makefiles :ocean::sailboat:, I think I got something working!

The following instructions are intended for the OPENMV4 target, which I believe corresponds to the OpenMV H7/OpenMV H7 R2 (please correct me if I’m wrong). For anyone reading in the future, I’m using revision d10c883e6557bd62bc105388360542407094ecdb of the source tree. This has not been thoroughly tested, but if I discover something I’m missing I’ll add a follow-up post to this thread.

Step 1

Download the source tree and the submodules using the instructions here: openmv/README.md at master · openmv/openmv · GitHub.

Step 2

Add a C++ source file to the project:

// src/omv/modules/new_custom_module.cpp
#include <algorithm>

extern "C" short biggest_short(short a, short b)
{
    return std::max(a, b);
}

Step 3

Tell the MicroPython makefile (which is invoked indirectly by running make in src/), to add a corresponding entry to the list of object files to generate:

# src/micropython/ports/stm32/Makefile, in the variable definition section
OBJ += $(BUILD)/modules/new_custom_module.o

Step 4

Tell the MicroPython makefile how to build that object file:

# src/micropython/ports/stm32/Makefile, in the build rules section
$(BUILD)/modules/new_custom_module.o: ../../../omv/modules/new_custom_module.cpp
	$(CXX) \
	  -Wall -Werror -Warray-bounds -Wdouble-promotion -Wno-float-conversion \
	  -mthumb -mcpu=cortex-m7 -mtune=cortex-m7 -mfpu=fpv5-sp-d16 -mfloat-abi=hard \
	  -O2 -fdata-sections -ffunction-sections -fno-inline-small-functions \
	  -fsingle-precision-constant -fsingle-precision-constant -fdata-sections -ffunction-sections \
	  -nostartfiles -nostdlib \
	  -c -o $(BUILD)/modules/new_custom_module.o \
	  ../../../omv/modules/new_custom_module.cpp

I lifted the flags from compilation commands I observed running make in verbose mode (make V=1 TARGET=OPENMV4 -C src). $(CXX) resolves to either arm-none-eabi-g++ or @arm-none-eabi-g++ (the same thing, but quieter).

Step 5

Compile the firmware as you would normally. The functions defined in the C++ file can now be used in MicroPython modules:

// src/omv/modules/examplemodule.c
#include "py/runtime.h"

short biggest_short(short a, short b);

// Expose functions that use `biggest_short` here.
// Don't forget to flip the "module enabled" bit in the `MP_REGISTER_MODULE` call!

Using Rust

no_std Rust seems to work as well:

// src/omv/modules/new_rusty_module.rs
#![no_std] 
#![no_main] 

use core::panic::PanicInfo;
use core::cmp;

#[panic_handler]
fn panic(_info: &PanicInfo<'_>) -> ! {
    loop {}
}

#[no_mangle]
pub extern "C" fn smallest_long(a: i32, b: i32) -> i32 {
    cmp::min(a, b)
}
# src/micropython/ports/stm32/Makefile, in the variable definition section
OBJ += $(BUILD)/modules/new_rusty_module.o
# src/micropython/ports/stm32/Makefile, in the build rules section
$(BUILD)/modules/new_rusty_module.o: ../../../omv/modules/new_rusty_module.rs
	rustc \
	  --emit=obj --target thumbv7em-none-eabihf -C opt-level=3 \
	  -o $(BUILD)/modules/new_rusty_module.o \
	  ../../../omv/modules/new_rusty_module.rs

I installed the Rust compiler and the Cortex-M7 code generator using rustup:

> curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
> rustup target add thumbv7em-none-eabihf