I have a class Colour
:
class Colour {
public:
std::byte r;
std::byte g;
std::byte b;
std::byte a;
};
Now if I have a function
void foo(const Colour& c);
I want to be able to call it by passing a string that represents the colour:
foo("red"); // become (255, 0, 0, 255)
foo("#00ff00"); // become (0, 255, 0, 255)
foo("hsl(240, 100, 50)"); // become (0, 0, 255, 255)
And of course I don't want to parse the string everytime, I would like the compiler to parse it and replace the string by the colour.
The problem is that we constexpr constructors, it can't have a body and must directly initialize the r, g, b, a values, or I could have a private member colour
so I can initialize it like this:
class Colour {
public:
constexpr Colour(const std::string& str) : colour(parseString(str)) {}
private:
InternalColor colour; // contains the r, g, b, a
};
constexpr InternalColour parseString(const std::string& str) {
// ...
// Parse the string and return the colour
}
But I want to have access to the r, g, b, a values directly, not with an indirection, and I don't want member functions like r()
.
So how can I parse the colour string at compile time and replace it with the colour? And yes I could call myself a constexpr function that return the color, but the idea is to directly pass a string.
I don't want to parse the string everytime,
Even with a
constexpr InternalColour parseString(const std::string_view& str)
None of the calls are in constant expressions, so apply at runtime (optimizer might help):
foo("red"); // become (255, 0, 0, 255)
foo("#00ff00"); // become (0, 255, 0, 255)
foo("hsl(240, 100, 50)"); // become (0, 0, 255, 255)
You would have to do:
constexpr Colour red{"red"}; // Parsed at compile time
foo(red);
// ...
with constexpr constructors, it can't have a body
C++11 rules are really strict. :/
Since C++14 rules have been relaxed a lot.
Even in C++11, you might use delegating constructor to by-pass that issue.
class Colour {
public:
constexpr Colour(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a) :
r(r), g(g), b(b), a(a) {}
// not `explicit`, as you want implicit conversion
constexpr Colour(const std::string_view& str) : Colour(parseString(str)) {}
constexpr Colour(const Colour& rhs) = default;
constexpr Colour& operator= (const Colour& rhs) = default;
public:
static constexpr Colour parseString(const std::string_view& str)
{
// constexpr parsing in C++11 might be non trivial,
// but possible (one return statement only :/ )
if (str == "red") { return {255, 0, 0, 255}; }
// ...
}
public:
std::uint8_t r;
std::uint8_t g;
std::uint8_t b;
std::uint8_t a;
};