Foreign Constructors

Simplifying complicated constructors.

Recently I found myself extending a game engine with font rendering support. Fonts have a lot of properties, so naturally I created a tidy structure to hold all parameters. Whenever drawing a piece of text: I'd simply pass this structure along.

A philosophy I follow, is one of programmer convenience. For example: I always specify default values; when you instantiate a class without any parameters, it is directly usable. Following from that, whenever you call an method or constructor - you should not have to specify not used parameters. This adds a practical issue: it requires all kinds of constructors. Here is my initial structure:

struct Font {
	std::string family = "monospace";
    float size         = 12.0f;
    Weight weight      = Weight::Bold;
    Slant slant        = Slant::Normal;
    Anchor halign      = Anchor::Center;
    Anchor valign      = Anchor::Center;
    Color color        = Color::White;  
    
    Font() {}
    Font(std::string font)  : font(font) {}
    Font(std::string font, float size)  : font(font), size(size) {}
    Font(float size, Color color) : size(size), color(color) {}
    Font(Color color) : color(color) {}
    Font(float size) : size(size) {}
    // [...] more constructors.
};


This is impractical and ambiguous, notice how halign and valign have the same type. Types have to be unique for parameter order invariant constructors (one could use Tiny Types). Quickly enough I removed the constructor madness and created instances the following way:

Font font;
font.weight = Weight::Bold;
font.color = Color::Red;
// and more configurations.

Unfortunately, such Font instance could never be declared constant. After all, we are modifying it post-creation. Sourcing inspiration from the visitor pattern, I'd like to introduce the foreign constructor to solve this issue:

struct Font {
    std::string family = "monospace";
    // [...] all other properties.
    
    // The foreign constructor.
    Font(std::function visit) {
        visit(*this);
    }

    // A few common constructors.
    Font(std::string family) {}
};

The foreign constructor allows instantiation with the following syntax:

class Button {
    const Font font = Font([] (Font& font) {
        font.weight = Weight::VeryBold;
        font.slant  = Slang::Oblique;
        font.halign = Anchor::Left;
    });
};

It requires some ASCII-art [](){} to get the C++ lambda syntax working, but it feels clean to me and gets the const-ness right. It is actually very similar to the ISO C99 syntax for label-based struct instantiation, but without the drawbacks (e.g., only works with build-in types, no default values and no constructors).

// Not legal in C++ (but will compile, probably)
const Example font = {
    .number: 10,
    .pi:     3.1415,
    .byte:   0xff
};

Enjoy foreign constructors.

Written by Gerard Meier on May 24, 2015.