A c++17 single header library for constructing and building classes inline.
Initialize a class and modify it in-place:
// Returns a Label with size: 200 x 50, and text: "Ok"
build([](Label& _){ _.set_size(200, 50); }, "Ok");
Or directly create a new raw or smart pointer
// Returns a new Label*
build_new([](Label& _){...}, "Ok");
// Returns a std::shared_ptr<Label>
build_shared([](Label& _){...}, "Ok");
// Returns a std::unique_ptr<Label>
build_unique([](Label& _){...}, "Ok");
We want to construct a widget with a button inside.
Standard approach:
auto widget = new Widget();
widget->set_color({255, 0, 0});
auto btn_add = new Button("+");
btn_add->set_action(Action::Add);
widget->add_child(button1);
auto btn_subtract = new Button("-");
btn_add->set_action(Action::Subtract);
widget->add_child(btn_subtract);
Blacksmith approach:
auto widget = build_new([](Widget& _) {
_.set_color({255, 0, 0});
_.add_child(build_new([](Button& _) {
_.set_action(Action::Add);
}, "+"));
_.add_child(build_new([](Button& _) {
_.action = Action::Subtract;
}, "-"));
});
Did you spot the mistake in the first approach? This one is hard to detect later on!
auto btn_subtract = new Button("-");
btn_add->set_action(Action::Subtract);
// ^^^^^^^ this should be btn_subtract
widget->add_child(btn_subtract);
Blacksmith aims to eliminate this type of error by encouraging a more declarative style of programming.
- Only write the type once! And don't worry about thinking of a unique variable name.
build([](Widget& _) { ... });
^^^^^^ ^
- Trailing arguments are forwarded to the constructor
build([](Label& _) { ... }, "Hello, world");
^^^^^^^^^^^^^^
-
All
build
functions areconstexpr
and are optimized away by the compiler. -
Build functions can accept a reference or the actual type
build_shared([](Label& _) {...}); // Ok
^^^^^^
build_shared([](const std::shared_ptr<Label>& _) {...}); // Ok
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
auto layout = std::make_shared<Layout>(LayoutDirection::Vertical);
auto outer_widget = std::make_shared<Widget>();
outer_widget->set_background({255, 255, 255});
outer_widget->set_padding(4);
outer_widget->set_size(100, 100);
auto inner_layout = std::make_shared<Layout>(LayoutDirection::Horizontal);
auto inner_label = std::make_shared<Label>("Red Box");
inner_label->set_background({255, 255, 255});
inner_label->set_size(300, 50);
inner_layout->add_child(inner_label);
auto inner_box = std::make_shared<Widget>();
inner_box->set_background({255, 0, 0});
inner_box->set_size(50, 50);
outer_widget->set_layout(inner_layout);
layout->add_child(outer_widget);
draw_layout(layout);
using namespace blacksmith;
draw_layout(build_shared(
[](Layout &_) {
_.add_child(build_shared([](Widget &_) {
_.set_background({255, 255, 255});
_.set_padding(4);
_.set_size(100, 100);
_.set_layout(build_shared(
[](Layout &_) {
_.add_child(build_shared(
[](Label &_) {
_.set_background({255, 255, 255});
_.set_size(300, 50);
},
"Red Box"));
_.add_child(build_shared([](Widget &_) {
_.set_background({255, 0, 0});
_.set_size(50, 50);
}));
},
LayoutDirection::Horizontal));
}));
},
LayoutDirection::Vertical));
#include <string>
#include <vector>
#include "blacksmith.h"
struct Pet
{
std::string species;
std::string name;
std::string full_name() const { return name + " " + species; }
};
struct Person
{
std::string first_name;
std::string last_name;
int age;
std::vector<Pet> local_pets;
std::vector<std::shared_ptr<Pet>> shared_pets;
std::vector<std::shared_ptr<Pet>> unique_pets;
std::vector<Pet *> raw_owned_pets;
~Person()
{
for (auto *pet : raw_owned_pets) {
delete pet;
}
}
};
int main()
{
using namespace blacksmith;
auto owner = build([](Person &_) {
_.first_name = "Jon";
_.last_name = "Doe";
_.age = 42;
_.local_pets = {{
build([](Pet &_) {
_.species = "Bat";
_.name = "Cricket";
}),
}};
_.shared_pets = {{
// Argument can be a reference
build_shared([](Pet &_) {
_.species = "Cat";
_.name = "Smelly";
}),
// Or a shared pointer
build_shared([](const std::shared_ptr<Pet> &_) {
_->species = "Dog";
_->name = "Hot";
}),
}};
_.unique_pets = {{
build_unique([](Pet &_) {
_.species = "Cat";
_.name = "Copy";
}),
build_unique([](std::unique_ptr<Pet> &_) {
_->species = "Cat";
_->name = "Bob";
}),
}};
_.raw_owned_pets = {{
build_new([](Pet &_) {
_.species = "Chicken";
_.name = "Arya";
}),
build_new([](Pet *_) {
_->species = "Otter";
_->name = "Hairy";
}),
}};
});