C++ Cookbook
Practical, copy-paste-ready recipes for C++ code generation. For the full API of each spec type, see Building Functions & Fields, Building Types & Enums, and Files & Projects.
Class with template
use sigil_stitch::prelude::*;
let body = CodeBlock::of("data_.push_back(value)", ()).unwrap();
let type_spec = TypeSpec::builder("Stack", TypeKind::Class)
.add_type_param(TypeParamSpec::new("T"))
.add_field(
FieldSpec::builder("data_", TypeName::generic(
TypeName::primitive("std::vector"),
vec![TypeName::primitive("T")],
))
.visibility(Visibility::Private)
.build()
.unwrap(),
)
.add_method(
FunSpec::builder("push")
.visibility(Visibility::Public)
.add_param(ParameterSpec::new("value", TypeName::reference(TypeName::primitive("T"))).unwrap())
.body(body)
.build()
.unwrap(),
)
.build()
.unwrap();
template <typename T>
class Stack {
private:
std::vector<T> data_;
public:
void push(const T& value) {
data_.push_back(value);
}
};
Using alias (C++ type alias)
use sigil_stitch::prelude::*;
let type_spec = TypeSpec::builder("StringVec", TypeKind::TypeAlias)
.extends(TypeName::generic(
TypeName::primitive("std::vector"),
vec![TypeName::primitive("std::string")],
))
.build()
.unwrap();
using StringVec = std::vector<std::string>;
Enum class
use sigil_stitch::prelude::*;
let type_spec = TypeSpec::builder("Color", TypeKind::Enum)
.doc("Available colors.")
.add_variant(EnumVariantSpec::new("Red").unwrap())
.add_variant(EnumVariantSpec::new("Green").unwrap())
.add_variant(EnumVariantSpec::new("Blue").unwrap())
.build()
.unwrap();
/// Available colors.
enum class Color {
Red,
Green,
Blue
};
Virtual method
C++ abstract classes with pure virtual methods require the extra_member escape hatch. Use FunSpec::emit() to render each method signature as a CodeBlock, then attach it to the type via extra_member.
use sigil_stitch::prelude::*;
use sigil_stitch::lang::cpp_lang::CppLang;
fn emit_fun(fun: &FunSpec) -> CodeBlock {
let lang = CppLang::new();
fun.emit(&lang, DeclarationContext::Member).unwrap()
}
let mut pub_section = CodeBlock::builder();
pub_section.add("%<", ());
pub_section.add("public:", ());
pub_section.add_line();
pub_section.add("%>", ());
pub_section.add_code(emit_fun(
&FunSpec::builder("area")
.is_abstract()
.returns(TypeName::primitive("double"))
.suffix("const")
.suffix("= 0")
.build()
.unwrap(),
));
pub_section.add_line();
pub_section.add_code(emit_fun(
&FunSpec::builder("~Shape")
.is_abstract()
.suffix("= default")
.build()
.unwrap(),
));
let type_spec = TypeSpec::builder("Shape", TypeKind::Class)
.doc("Abstract shape base class.")
.extra_member(pub_section.build().unwrap())
.build()
.unwrap();
/// Abstract shape base class.
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
Namespace wrapping
Use FileSpec::add_raw to wrap generated code in a namespace block.
use sigil_stitch::prelude::*;
let mut b = CodeBlock::builder();
b.add("int square(int x) {", ());
b.add_line();
b.add("%>", ());
b.add("return x * x;", ());
b.add_line();
b.add("%<", ());
b.add("}", ());
b.add_line();
let block = b.build().unwrap();
let file = FileSpec::builder("math.hpp")
.header(CodeBlock::of("#pragma once", ()).unwrap())
.add_raw("namespace math {\n")
.add_code(block)
.add_raw("\n} // namespace math\n")
.build()
.unwrap();
let output = file.render(80).unwrap();
#pragma once
namespace math {
int square(int x) {
return x * x;
}
} // namespace math