Throughout this cookbook, you've seen me use Affix::Build to generate shared libraries on the fly from C. However, building shared libraries isn't a concept limited to just C so neither are the capabilities of Affix::Build.
Affix::Build, first introduced in our very first chapter, is a cross-platform, multi-lingual compilation wrapper. While it supports over 15 languages (including Rust, Go, Fortran, and D), it is particularly useful for C and C++ because it abstracts away the differences between GCC, Clang, and MSVC, handles operating system extensions (.dll, .so, .dylib), automatically locates the correct driver to link the standard libraries properly, and applies critical flags like -fPIC where required.
In this chapter, we'll demonstrate that as we build a library that uses the C++ Standard Library (STL) to reverse a string in place.
The Recipe
use v5.40;
use Affix qw[:all];
use Affix::Build;
# 1. Configure the Compiler
# We enable debug mode to see the underlying compiler commands
my $compiler = Affix::Build->new( name => 'str_util', debug => 1 );
# 2. Add C++ Source
# We pass a reference to a string containing the source code.
# We must specify 'lang => cpp' so the compiler knows to use a C++ driver.
$compiler->add( \<<~'CPP', lang => 'cpp' );
#include <algorithm>
#include <string>
#include <cstring>
extern "C" {
// We use C linkage so Affix can find the symbol easily
void reverse_it(char* buffer) {
if (!buffer) return;
// Use high-level C++ features
std::string s(buffer);
std::reverse(s.begin(), s.end());
// Copy back to the raw buffer
std::strcpy(buffer, s.c_str());
}
}
CPP
# 3. Compile and Link
# This generates the shared object in a temp folder and returns the path.
my $lib_path = $compiler->link;
say 'Library compiled to: ' . $lib_path;
# 4. Bind and Call
affix $lib_path, 'reverse_it', [String] => Void;
my $text = 'Affix makes C++ easy';
reverse_it($text);
say $text;
Output:
[Affix] Exec: g++ -shared C:/Users/S/AppData/Local/Temp/Yx9npzzak9/source_001.cpp -o C:/Users/S/AppData/Local/Temp/Yx9npzzak9/str_util.dll.1
Library compiled to: C:/Users/S/AppData/Local/Temp/Yx9npzzak9/str_util.dll.1
ysae ++C sekam xiffA
How It Works
-
1. Language Detection
When you call
$compiler->add( ..., lang => 'cpp' ), the compiler updates its internal linking strategy. It knows that standard C compilers (gcc) cannot link C++ standard libraries (libstdc++) by default. It resolves the correct driver on your system (preferringg++orclang++) to ensure the STL is available. -
2. Inline Source
Passing a SCALAR reference (
\<<~'CPP') tells the compiler to write that content to a temporary file before compiling. You can also pass a standard file path (e.g.,$compiler->add('src/utils.cpp')), in which case thelangparameter is optional (auto-detected by extension). -
3. Platform Abstraction
The call to
$compiler->linkperforms a lot of heavy lifting:- Windows: It knows to use the
.dllextension and creates a PE binary. - Linux/Unix: It uses
.soor.dylib, adds the-fPIC(Position Independent Code) flag required for shared libraries, and ensures the library name is prefixed withlibif required by the OS loader.
- Windows: It knows to use the
Bonus: The Polyglot Strategy
Affix::Build can mix languages in a single library. If you add files from different languages (e.g., C and Rust), the compiler switches strategies:
- It compiles every source file into a static object file (
.oor.a) using the appropriate compiler for each language (rustcfor Rust,ccfor C, etc.). - It invokes the system linker to combine all static objects into one shared library.
# Example: Using a Rust function inside a C wrapper
$compiler->add( 'src/calc.rs' ); # Contains #[no_mangle] pub extern "C" fn add...
$compiler->add( 'src/main.c' ); # Calls 'add' from Rust
$compiler->link; # Links them into one DLL
We'll revisit this concept in future chapters.
Kitchen Reminders
-
Compiler Flags
You can optimize your build or include external headers using the
flagsparameter in the constructor.my $c = Affix::Build->new( flags => { cxxflags => '-O3 -I/usr/local/include/my_lib', ldflags => '-L/usr/local/lib -lmy_lib' } ); -
Windows MinGW
If you are using Strawberry Perl,
Affix::Buildautomatically handles thewhole-archivelinking issues often encountered when bundling static C++ libraries into a DLL, ensuring no symbols are stripped during the link phase.