Showing posts with label metaprogramming. Show all posts
Showing posts with label metaprogramming. Show all posts

Sunday, October 9, 2016

constexpr offsetof, a practical way to find the offset of a member in a constexpr

constexpr function to find the offset of a data member

I’ve been working on high performing code and needed to know at compile time whether the offset of a member from the beginning of its containing object is exactly a multiple of a SIMD vector size.

There exists the standard C-library macro offsetof swept into C++. For something as simple as a concrete structure and member, we can get its offset:

#include <cstddef>

struct ConcreteAggregate {
   double member1;
   int concreteMember;
};

static_assert(8 == offsetof(ConcreteAggregate, concreteMember), "");

However, offsetof is extremely limited, this slight change, of moving "member1" to a concrete base class, is already too much for offsetof:

#include <cstddef>

struct Base { double member1; };
struct Derived: Base { int concreteMember; };

static_assert(8 == offsetof(Derived, concreteMember);

Fails to compile. offsetof requires the type to be of standard layout, which for C++ metaprogramming already makes it useless.

Besides, the macro requires the name of the member. In templates you don’t know the name of the members, what you can have is its address as member pointer.

There’s a simple trick that would give the answer, if we had an object of the given type, we could compare its address to the address of the member:

template <typename T, typename MT, MT T::*MPtr>
std::size_t offset_of(const T &value) {
    return (char *)&(value.*MPtr) - (char *)&value;
}

std::size_t fun(const Derived &d) {
    return offset_of<Derived, int, &Derived::concreteMember>(d);
}

Will “work”, but alas, not in a constexpr. It turns out casting is forbidden in constexprs. Besides, that function is not really very useful since it requires a value of the given type. It should not require it, because what does it matter, for the purposes of determining the offset of a member, where is the value? – A null pointer can not be dereferenced, not even in a sort of un-evaluated context such as a constexpr.

I’ve been trying to find a way to solve this issue in an standard compliant way, and I am almost certain there is no way because there are no conversions allowed in standard C++ that would give us “char pointers” to calculate the bytes.

However, I think I have devised a portable solution: through a sleight of hand the code tricks the compiler into using rules for constant expressions of C++ 98 which allowed these casts. Because current compilers compile C++ 98, this trick in practice should work:

namespace detail {

template<typename T> struct declval_helper { static T value; };

template<typename T, typename Z, Z T::*MPtr>
struct offset_helper {
    using TV = declval_helper<T>;
    char for_sizeof[
        (char *)&(TV::value.*MPtr) -
        (char *)&TV::value
    ];
};

}

template<typename T, typename Z, Z T::*MPtr>
constexpr int offset_of() {
    return sizeof(detail::offset_helper<T, Z, MPtr>::for_sizeof);
}

The three keys in this code are:

  1. Using old-style constant expressions in the declaration of offset_helper::for_sizeof
  2. Creating our own declval equivalent since we want to use the value
  3. In the constexpr using sizeof

Friday, September 2, 2016

Even safer bitfields

Safer bitfields

I was reading “Preshing on Programming”'s entry on bitfields; he mentions the lack of runtime checks for overflow on normal bitfields, and sets about dealing with the problem by creating a template and some preprocessing glue to implement the run-time checks.

I thought this effort is valuable and interesting, the bitfield feature of C++ is very hostile to the usual template programming: For example, in a template, how do you get the total number of bits in two adjacent members of a bitfield structure? Bitfield-structures in general are not very useful. One of my fundamental tenets about how to get performance and reliability is by expressing into code sophisticated invariants, I think, for example, the size of a member of a bitfield is something that should be made available to templates, but it is not, which leads to very dangerous ad-hoc code to use them. This is another instance of the missing parts regarding introspection in C++.

John Lakos advocates offering to users your library in several versions, versions that will have the same semantics except performance so that they can trade performance for more thoroughness in the checking of their correct usage. This is very useful for development and debugging. Using the normal templates of C++ it is trivial to introduce a tiny bit of conditional compilation, with very clear semantics, to enable or disable these run-time checks. Since bitfield-structs are hostile to templates, you can’t easily turn on or off safety checks if you implement them.

Preshing’s implementation of the supporting BitFieldMember is sound for the purpose of supporting runtime safety checks, and it would be straightforward to adapt it disable the checks for no performance loss. However, there is an important guarantee absent from his preprocessing macros, that the offset of a field is exactly the sum of sizes of the preceding fields. Another feature I wanted to provide support to is more compile-time introspection (reflection).

Here is a template used to support the rest of the work in bitfields I made:

#include <array>

template<typename T, unsigned... S> struct base {
    constexpr static std::array<unsigned, sizeof...(S)> sizes = { S... };
    constexpr static unsigned displacement(unsigned ndx) {
        return ndx ? sizes[ndx - 1] + displacement(ndx - 1) : 0;
    }
    T value;
};

So far, just a template that gives you a choice for the integral used to hold the bitfield, an array of field sizes and a constexpr function, displacement, to tally the sizes of the predecessors.

We can now use Preshing’s BitFieldMember template, for example:

union manually_done_bitfield {
    using element_t = long;
    using base_t = base<long, 4, 3, 5>;
    BitFieldMember<element_t, base_t::displacement(0), 4> fourBits;
    BitFieldMember<element_t, base_t::displacement(1), 3> threeBits;
    BitFieldMember<element_t, base_t::displacement(2), 5> fiveBits;
};

The manual part of setting the bit sizes to the same as the declarations of the members, as well as the progression of indices, can be coded using boost seq preprocessing:

// Generic macros not specific to this article
#define UNTUPLE_2_1(a, b) a
#define UNTUPLE_2_2(a, b) b
#define PP_SEQ_TUPLE_2_2(s, d, element) UNTUPLE_2_2 element
#define PP_SEQ_ENUM_UNTUPLE_2_2(r, data, e) ,UNTUPLE_2_2 e

// Macros actually used from boost seq preprocessing
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

// Macro to declare a member
#define PP_SEQ_I_MAKE_BITFIELD(r, data, i, element) BitFieldMember<element_t, base_t::displacement(i), UNTUPLE_2_2 element> UNTUPLE_2_1 element;

// The actual union defining macro
#define SAFE_BITFIELD(name, type, fields)\
    union name {\
        using element_t = type;\
        using base_t = base<type BOOST_PP_SEQ_FOR_EACH(PP_SEQ_ENUM_UNTUPLE_2_2, ~, fields)>;\
        BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_I_MAKE_BITFIELD, ~, fields)\
    }

// The example above expressed as a boost seq
#define example ((fourBits, 4))((threeBits, 3))((fiveBits, 5))

SAFE_BITFIELD(automatic, long, example);

Now, beautifully, we can use static_assert to our hearts’ content:

static_assert(7 == automatic::base_t::displacement(2), "");

From here, there are ways to map the fields to their indices, etc.

Tuesday, December 29, 2015

The path to the innermost nature of things: metaprogramming

Metaprogramming

I once had the privilege of attending a presentation by Stephen Dewhurst on the subject of metaprogramming. It is from this event that I learned metaprogramming was not designed into C++ but discovered. That is, our predecessors trying to squeeze performance out of the language developed idioms that would have the compiler go beyond merely translating to object code, to also perform useful computation and insert the results to the object code, so that they would not have to be computed at run time; these techniques grew and became a new way to program; C++'s type system is so powerful that it is “turing complete”, that is, it can be used to program anything. This does not mean that it is practical, especially not that it is easy to do it.

Expressing relationships

There are times when meta programming is helpful, not just to bring to compilation time computation that otherwise would be done at run time, but computation that helps improve software quality. Most software elements have many relationships to other software elements, better programming languages allows the expression and maintenance of more and more abstract of those relationships. It is this interest, of improving software quality by expressing sophisticated relationships between software elements, what is my main interest in metaprogramming. I have introduced BOOST preprocessing in the last two articles illustrating techniques whose aim is to express relationships between software elements that can not be expressed within the pure language, I thought on reviewing those articles that I should have begun with relationships that can be expressed, hence I allow myself to postpone once again the description of how to use boost preprocessing.
By way of example, let us discuss the data structure of the singly linked list. Not caring about the operations, the data layout may be:
template<typename T> struct SListNode { T value; SListNode *link; };
struct SList { SListNode *head; unsigned size; };
However, this can be wasteful. In today’s architectures a pointer is typically 8 bytes long, if the element type T is as small as a single byte, because of very important alignment considerations, the size of the node will be 16 bytes. That’s an overhead of 16 to 1…

Type inferences

Can this be implemented better? Most architectures will have the same performance dereferencing a pointer as dereferencing a pointer plus an offset. That is, *pointer performs as well as *(pointer + offset). This is the case on the AMD64 architectures, and in Intel’s 32 bit and AMD64, there is another mode of addressing with the same performance, the “Scale Index Base”: *(pointer + index*element_size + offset) is as performing as *pointer provided the element_size is any of 1, 2, 4, or 8. Incidentally, in intel32/AMD64, the fastest way to multiply by a constant such as 5, (or 3 or 9) is to “load the effective address” of the operand register + 4*register, for example, lea (rax + 4*rax), rbx.
If we are not going to hold more than 4 giga elements, using a pointer to discriminate is wasteful, so it may be practical to create a memory arena for our lists, just a large array of bytes. Then, our node could be represented like this:
template<typename T> struct SListNode {
    static SListNode *arena; ///< a class-member common to all lists of this type T
    T value;
    unsigned arenaIndexOfNext;

    SListNode &next() { return arena[arenaIndexOfNext]; }
    unsigned currentIndex() const noexcept { return this - arena; }
};
If the total number of nodes will be less than 2^16, thus we won’t have more than 2^16 indices, and the size of the type T is 2 bytes, our current implementation of using an unsigned (typically 4 bytes) forces the node to be of 8 bytes when it really requires 4; where I am going with this line of reasoning is that the optimal type used for the index is dependent on the size of the type T and the maximum number of nodes to be supported. Conceivably, we can manually implement the structure for each possible combination:
  1. 1 byte element, up to 256 nodes: use a byte for index
  2. up to 2 byte elements and less than 2^16 indices: use a two-byte for the index
  3. up to 4 byte elements use a four-byte index
  4. more than 4 byte elements use a pointer

Generic programming

And this is in the case we want to keep the value and the link together, if we chose to have separate arenas for the values and the links represented as indices, we have the whole cartesian product of sizes: for the element type size < 2, < 4, < 8, >= 8 and the total number of elements, < 2^8, < 2^16, > 2^16 or 12 different cases that are essentially the same code, just substituting the appropriate types and constants. The language has templates, both with type parameters and non-type parameters to accomplish coding the mechanism (singly linked list through arenas and indices). In that sense it is generic, in which the implementation may not care about the details of the parameters.
To use the generic implementation the programmer/user still has to make a choice: what is the type of the index that best suits the number of possible indices and the type of the elements; as explained in the preceding list, there is a clear way to select the right index. Since both the element type and the maximum number of indices are not known when writing the code to select the right index type, in effect that code will reason about elements that will be given to the program at a later time, that is, they work with elements of the program itself, hence metaprogramming.
So, generic is when the specific types (or constants) don’t matter; meta is when the parameters to templates will be decided based off other types the programmer gives, of course that each are the other to some degree. Alexander Stepanov, the person with the largest share of the credit for the invention of Generic Programming said in this zero-waste interview that he “[believes] it is necessary to design a programming language from the ground up to enable generic programming in a consistent way”. It is safe to say that the higher one goes into generic abstraction, that is, into proper meta-programming territory, the inconsistencies become much harder to overcome.
It begs the question, is it worth at all fighting this accidental complexity on top of the already huge inherent complexity? I would say yes, it is, because the inherent complexity (being able to program in a generic way) is the ultimate way to make software, to make something generic, one needs to understand it to its core, thus the inherent complexity of generic programming is how to capture the innermost nature of what’s being programmed. With regards to the accidental complexity, I draw from my personal experience in that I have had to exercise myself fully into getting around the inconsistencies; I have used techniques of functional programming, of logic programming, of artificial intelligence, and mathematics, more mathematics and even more mathematics. I have reached a much higher understanding of what programming is all about thanks to my interest in trying to accomplishing genericity. Surprisingly, I have actually succeeded at getting great practical results after it all. Think about all of the frustrations that needed overcoming to program the STL so that it could be approved into the 1998 standard, but then look at the monumental accomplishment… just to name one example, C++, to my knowledge, is the only language that has a managed array type (std::vector) with essentially zero performance overhead over unmanaged arrays… and in the 17 years and counting since it has been part of the standard, all other languages still “don’t get it”, for example, Java, C# have “generics” which are but pathetic syntactic sugar.
That is, the fighting itself has been an excellent school.
I find the subject so fascinating that it motivates me to make community out of sharing our findings.
Back to the pedestrian dydactic example, here’s where meta programming is helpful:
  1. lets the programmer express the relationships between the element size and the index size (or whether to use pointers),
  2. lets the compiler choose the implementation case that best suits the size of the type and the maximum number of elements,
  3. relieves the programmer from the responsability to choose the index (and thus the chance for her/him to make a poor choice)
  4. keeps the option for further refinements
I trust the reader to be able to program the generic arena solution given the optimal type for the indices, and being able to specialize for the case in which it makes more sense to use the standard node structure. For dydactical purposes, this is what will give the adequate index type for the arena implementation in which the nodes are the composition of the element and the index. The template alias meta::Index that takes a template type parameter (what is going to be held in lists) and the maximum number of nodes for lists of that type; this is defined below:
namespace meta {
    template<unsigned> struct type_size { using type = unsigned long; };
    template<> struct type_size<4> { using type = unsigned; };
    template<> struct type_size<2> { using type = short unsigned; };
    template<> struct type_size<1> { using type = unsigned char; };

    constexpr unsigned powerOf(unsigned base, unsigned exponent) noexcept {
        return exponent ? base*powerOf(base, exponent - 1) : 1;
    }

    constexpr unsigned logCeiling(unsigned base, unsigned v) noexcept {
        return v <= 1 ? 0 : 1 + logCeiling(base, (v + base - 1)/base);
    }

    template<typename T> constexpr T max(T a, T b) { return a < b ? b : a; }

    template<typename ElementType, unsigned MaxIndex> using Index =
        typename type_size<
            powerOf(
                2,
                max(
                    logCeiling(2, sizeof(T)),
                    logCeiling(256, Max)
                )
            )
        >::type;
}
Just a few definitions and the compiler will be able to pick the right index type. Provided you have an implementation of a heap manager, you’d have reduced the space overhead of containing elements in lists to its optimum. Since indexing as opposed to direct dereferencing has no penalty, the net effect of generic programming plus metaprogramming are
  1. Generics make it so that two implementations (normal single linked list and arenas) reduce the chances for errors of doing things manually or the different varieties to drift apart
  2. The space overhead is minimal,
  3. the increase in space efficiency translates into net speedup due to more effective caching
  4. No other performance penalty
  5. Increased compilation time
In essence, metaprogramming has been used in this example to express the relationship between the optimal index type to the element type and the maximum number of nodes that will be supported. The benefit or the importance of being able to code this relationship is trivial, however, the relationships that can be expressed with the mechanisms of metaprogramming are of arbitrary complexity, and they lead to truly great benefits.

Monday, November 30, 2015

Introspection, preprocessing and boost preprocessing

I was reading our blogspot neighbor Scott Meyers, “The Brick Wall of C++ Source Code Transformation”, where he discusses one of the worst problems in C++: The overreliance on the preprocessor. For a few years now I have been using the preprocessor to palliate the deficiencies of the introspection capabilities. I hope that would be another class of legitimate use of the preprocessor such as to #include headers because there is no concept of modules and simple management of versions through conditional compilation, like when one asks if the code is being compiled with G++, #if defined __GNUG__. I am not entirely sure this use is legitimate, but it seems the least bad of the practical options. Let us get to know the animal --monster-- of the preprocessor in more detail, to appreciate its cost this time. In later articles I hope to make good exhibits for the zoo, in which this animal shows its potential.

What is the problem?

The preprocessor is a source code transformation language, the problems are that it is a very poor language and inserting a source code transformation layer between the programmer and the compiler makes their lives much harder. Concretely, it makes making tools hard to nearly impossible, makes compilation much slower, it makes source code much harder to understand and it invites problems.

Headers

Although the language does not support modules we still need to import their interfaces, that’s why we speak of declarations and definitions. For example, #include is equivalent to copying and pasting the included file into the current file. However, the declarations in the included header are affected by the context carried over, and this opens a can of worms. Let’s say in file header1.h there is this code:
namespace library1 {

// some declarations
#define ITEM product
// more declarations, ITEM is never undefined

}
Later, another header may have this content:
namespace library2 {

// some declarations
struct ITEM {
    void not_inline_function();
    // …
};

// more declarations
}

Then, if a file.cpp has this content:
#include “header1.h”
#include “header2.h”

Every mention to “ITEM” will in reality refer to a product. The program may even compile, since the declaration struct ITEM will be silently converted to struct product in a different namespace, and within that namespace, all the mentions to ITEM will be consistently changed to product. It may even link!, if all the uses of ITEM are for data members and inlined functions. However, months later some change happens to the code, somebody uses the member function not_inline_function, and gets stomped with the link error undefined reference to not_inline_function

Back when people were switching from 32 bits to 64 bits, it was frequent for code to assume a pointer and an integer were of the same size, so, there was lots of people that would #define int long and rebuild. Of course, things may appear to work, the developer may be praised for how fast he did the migration, but things will eventually explode catastrophically because some “who knows what” library function not recompiled assumed 32 bits for an integer and it got 64.

Perhaps my examples are not good, in any case, the point I am trying to make is that a header and its corresponding binary library must be exactly in sync, otherwise, things may seem to work until they explode; because headers are vulnerable to the context in which they are #included, going through the preprocessor introduces all sorts of nasty possibilities for subtle errors.

Another problem, of a practical nature, is that the compiler is forced to compile the #included headers every time they appear, because since the interpretation of a header depends on the exact code that precedes its inclusion, there is no remedy but to do it all over! Note: this is what makes precompiled headers worthless: To guarantee the headers will all have the same meaning, the programmers must put together all the headers they could potentially include into a single ‘universe’ header, which is big, fat, greasy with leaking references to implementation details, couplings to implementation details and very low cohesiveness. Then, in all places were well thought-off headers would be included, to include the universe instead.

Versions

The other legitimate preprocessor case I mentioned of conditional compilation and versioning suffers from not having a way to guarantee that a prebuilt binary is compatible with the current source code and compilation options, leading in practice to the wasteful re-compiling and re-linking; or much worse, that apparently things work but in reality have catastrophic bugs: Let’s say a.cpp gets compiled assuming struct Foo has a member int member but because of subtle interplay of conditional compilation, in b.cpp struct Foo may have a member long member. Things may appear to be fine, but the layout of Foo is different, somewhere some code may think it is X bytes long, and some other Y bytes long, and then all bets are off.

These are not new problems, they are actually over forty years old. The culprit is that the language does not offer any way to guarantee declarations for library users will respect the assumptions made in their compiled/linked binary implementations. It is truly embarrassing, and I’ve not seen any prospects for a solution, not even for C++17 when some form of modules are being discussed.

Toolability

Because of language deficiencies, we still need to use the preprocessor, and this means that the only tools that can be developed for the language must in some way or other be also compilers. For example, imagine an IDE tool that wants to help with autocompletion of function calls. To be useful, the tool must be able to add applicable functions that are the result of macro expansions. Thus the tool must be able to preprocess, on top of all the other pure language requirements. It also must be able to know which macro expansions correspond to what places in the source code. What about function-like macros? when doing auto completion, is it going to offer the macro or only its expansion?

Introspection

I hope the claim that the preprocessor is a real problem has been substantiated, however, for all of its expressiveness the language has deficiencies that are covered by the preprocessor, thus it may be legitimate to continue to alleviate the deficiencies through the preprocessor. A consistent set of introspection features is sorely lacking. By introspection I mean what commonly is referred to as “reflection”, when there are ways in which programming constructs can reason about themselves. For example, in Java it is trivial to ask a class for its data and function members; it is possible to know everything there is to know about them. In C++ there is a collection of ad-hoc introspection capabilities, such as whether dynamic_cast of a pointer returns zero or not, the typeid, the sizeof operator and the very powerful collection of pattern matching capabilities of templates. For all of their might, it is simply not possible to know even the number of data members in a plain structure. It is not possible to know, from within the program itself, the count of enumerations in an enumerated type, nor what are the identifiers used, nor the mapping of enumerations to integer values, nor their reverse mapping.

The only option to accomplish introspection capabilities is to develop idioms or conventions so that we can plug some of the capabilities mentioned above.

The use case that led me down the path of using boost preprocessing was the simple need of converting enumerated values to strings. For example:
enum Enumeration {
    VALUE1 = 9,
    VALUE2 = 8,
    VALUE3 = 2
};

How to implement something like Enumeration::count() that will tell you the number of enumerated values, or Enumeration::values(), or Enumeration::identifiers()? This would need to be done manually. Typically, one wants to make something like std::ostream &operator<<(std::ostream &output, Enumeration value);, this is what we repeatedly waste our time doing:
std::ostream &operator<<(std::ostream &output, Enumeration e) {
    switch(e) {
        case VALUE1: output << “VALUE1”; break;
        case VALUE2: output << “VALUE2”; break;
        case VALUE3: output << “VALUE3”; break;
    }
    return output;
}

It does not look like much work, and it isn’t, but the real problem is that some day somebody will want to put VALUE4=13, forget to map it, and then the code won’t work. Also, somebody will just copy and paste forgetting to change the number:
std::ostream &operator<<(std::ostream &output, Enumeration e) {
    switch(e) {
        case VALUE1: output << “VALUE1”; break;
        case VALUE2: output << “VALUE1”; break;
        case VALUE3: output << “VALUE1”; break;
    }
    return output;
}

The compiler is happy. Your tests will be happy too, but in the critical log entry for your application, whenever VALUE2 appeared you’ll see VALUE1 written and be mystified…

Doctrine

I don’t like guranteeing relationships between software components manually, it is the same thing as using explicit new and delete instead of smart pointers; it is error prone. I like C++ because it lets me express fairly non-trivial relationships so that the compiler will guarantee them for me. When pure C++ is not sufficient, I try hard to find the least meta-C++ before resorting to do things manually. This element of my doctrine has served me very well. Even if my choices to represent relationships look very ugly, at least they are expressed, subject to improvement, and handled automatically; over time, things look less ugly, because you learn how to express yourself better, reducing accidental complexity and the inherent complexity is reduced once you realize it is necessary.

None of these happen with manual coding of relationships.

In general, I have reached to the conclusion that lists of identifiers are inexpressible in pure C++. Thus we have to look beyond for ways to handle lists of identifiers. Is it possible to express them using the preprocessor?

Boost preprocessing

Four years ago I discovered boost preprocessing while getting acquainted with the code of a system new to me, a genius coworker had coded a general solution to declare, define and print enumerations which used boost preprocessing, and at the same time, before C++11 his solution was capable of expressing enumerations in their own scope and based off user-specified integer types.

My old coworker got to different choices to what I will propose, but the idea is essentially the same. Let’s work toward the refined solution I made, illustrating each objective and solution.

Let us begin with the enumeration shown above. To be able to “reason” about that enumeration, at the bare minimum we need something that converts enumerated values to their identifier as a string. We need little else; these few things can be put together in an structure, the way in which we typically bind together related compile-time constructs. Let us associate the enumerated values to their strings by using a simple std::array:
#include <array>

struct demo {
    using integral_type = unsigned char;

    enum enumeration {
        VALUE1 = 8,
        VALUE2 = 9,
        VALUE3 = 2
    };

    using map_type = std::unordered_map<integral_type, const char *>;

    static constexpr unsigned count = 3;

    static constexpr std::array<typename map_type::value_type, count> values =
    {{
        { VALUE1, "VALUE1"}, { VALUE2, "VALUE2" }, { VALUE3, "VALUE3" }
    }};
};

demo has integral_type, enumeration, map_type, count and values, that I think serve a very clear role. Pure C++ won’t let us express a relationship between enumeration and values, but there is a way, which we will illustrate momentarily. In any case, with these we can implement any number of interesting introspection capabilities:
  1. Represent enumerated values using the integral_type
  2. All of the value-type services expected: construction, comparison, etc.
  3. It is possible to convert enumerated values to strings and viceversa
  4. Iteration over all the valid values of the enumeration
  5. All of these operations can be implemented to run very fast and potentially as compile-time constructs, that is, as if the language provided these features out of the box.
Let us augment the demo struct with many straightforward features through a template that combines some of the members into useful things. Please observe that indeed we got away with making almost all the things constexpr, noexcept:
#include <unordered_map>

namespace zoo {

template<typename E> struct smart_enumeration: E {
    using typename E::integral_type;
    using typename E::enumeration;

    integral_type code;

    constexpr smart_enumeration() noexcept: code(E::values[0].first) {}

    explicit constexpr
    smart_enumeration(integral_type v) noexcept: code(v) {}
    
    smart_enumeration(const smart_enumeration &) = default;
    
    explicit constexpr
    smart_enumeration(enumeration e) noexcept: code(e) {}

    smart_enumeration &operator=(enumeration e) noexcept
    { code = e; return *this; }

    constexpr operator integral_type() const noexcept { return code; }

    constexpr bool operator==(smart_enumeration e) const noexcept
    { return code == e.code; }
    constexpr bool operator!=(smart_enumeration e) const noexcept
    { return not (*this == e); }

    operator const char *() const {
        static const auto copy = E::values;
            // Note: this copy prevents having to "define" E::values
        static const std::unordered_map<integral_type, const char *>
            mapping(copy.begin(), copy.end());
        auto resultFind = mapping.find(code);
        if(mapping.end() == resultFind) { return nullptr; }
        return resultFind->second;
    }

    bool valid() const { return nullptr == static_cast<const char *>(*this); }
};

}

Note: Yeah, none of the constexpr, noexcept, and even const above are superflous: The compiler does not automatically allow the use of a non-constexpr function in compile-time expressions, nor deduces noexcept automatically; and because it was deemed a small mistake of C++ 11 that constexpr implies const in C++ 14 it doesn’t. It is a shame since all of these annotations add clutter and are deducible by the compiler.

Note2: The conversion to string is implemented not as a compile-time function because it uses an unordered_map; however, with enough effort it is possible to implement a compile-time map, even in C++11, just that it is not practical.

Now, the implementation of things like the insertion operation:
#include <istream>

namespace zoo {

template<
    typename E
> std::ostream &operator<<(std::ostream &output, zoo::smart_enumeration<E> e) {
    const char *ptr = e;
    if(ptr) { output << ptr; }
    return output;
}

}

Let us write a non-inline function that the compiler is forced to translate to drive enough of the implementations we have, also a “main” to prove we don’t have linking issues:
#include <iostream>

void drive(zoo::smart_enumeration<demo> d) {
    using se = zoo::smart_enumeration<demo>;
    static_assert(se().code == 8, "");
        // constexpr default constructor
    static_assert(noexcept(se()), "");
        // the default constructor is noexcept
        // Proven the constructor by integral and by enumeration are
        // constexpr and noexcept
    static_assert(noexcept(se(d)), ""); // default copy constructor is noexcept
    static_assert(9 == se(se(demo::VALUE2)).code, ""); // also constexpr

    static_assert(noexcept(se(8) == se(9)), ""); // equality comparison noexcept
    static_assert(noexcept(se(8) != se(9)), ""); // different is also noexcept
    static_assert(!noexcept(static_cast<const char *>(se())), "");
        // however, the conversion to const char * can throw
    std::cout << d << d.valid(); // All compiles
}

int main(int argc, const char *argv[]) { return 0; }

So, if we are able to supply the members that smart_enum requires, the same we put in demo, then forever we will be able to automatically get all the other stuff, and use our proto-introspection to implement lots of other things. For example, the backward map (string to enumeration) implemented in construct_smart_enum(std::string):
namespace zoo {

template<
    typename F, std::size_t N
> std::unordered_map<std::string, F> backward_map(
    std::array<std::pair<F, const char *>, N> argument
) {
    std::array<std::pair<std::string, F>, N> initializer;
    for(unsigned i = argument.size(); i--; ) {
        initializer[i].first = argument[i].second;
        initializer[i].second = argument[i].first;
    }
    return
        std::unordered_map<std::string, F>(
            initializer.begin(), initializer.end()
        );
}

template<typename E> smart_enumeration<E> construct_smart_enum(std::string s) {
    using se = smart_enumeration<E>;
    const static auto copy_for_gcc = E::values;
    const static auto reverted_map = backward_map(copy_for_gcc);
    return se(reverted_map.find(s)->second);
}

}

And the test, that also shows a possible way to use it:
smart_enumeration<demo> something()
{ return construct_smart_enum<demo>("VALUE2"); }

There only remains to show the magical incantation that will bind the identifiers to the enumeration values:
PP_SMART_ENUMERATION(
    demo,
    unsigned char,
    ((VALUE1, 8))((VALUE2, 9))((VALUE3, 2))
);

The expansion of that macro call will generate the code we wrote for demo above. This is the prestidigitation I just did in slow motion:
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>

#define PP_SEQ_META_CALL(r, MACRO, element) MACRO element

#define PP_MAKE_MAP_PAIR(identifier, value) { value, #identifier },
#define PP_MAKE_ENUMERATION_PAIR(identifier, value) identifier = value,

#define PP_SMART_ENUMERATION(name, integer_type, identifiers)\
struct name {\
    using integral_type = integer_type;\
    enum enumeration {\
        BOOST_PP_SEQ_FOR_EACH(PP_SEQ_META_CALL, PP_MAKE_ENUMERATION_PAIR, identifiers)\
    };\
    using pair_type = std::pair<integral_type, const char *>;\
    static constexpr unsigned count = BOOST_PP_SEQ_SIZE(identifiers);\
    static constexpr std::array<pair_type, count> values = {{\
        BOOST_PP_SEQ_FOR_EACH(PP_SEQ_META_CALL, PP_MAKE_MAP_PAIR, identifiers)\
    }};\
}

Not entirely self-explanatory? of course, and I have not even gone over the things that make this code break, but this is a great opportunity to leave the article in a cliff hanger…

Monday, October 26, 2015

Complications

C++ suffers from extremely bad choices for its defaults, this makes it very hostile to beginners.  It also shows its age.  It is good to have a good understanding of the complications, to learn to avoid them.  Fortunately, the community has devised idioms and conventions to deal with them.  This won't be a complete list, and it is probably too much to detail the problems and the strategies for their solutions within the same article, but let us start with some examples:
  • The most dangerous feature may be naked pointers.  Unfortunately, their use tends to be encouraged by novices leading the way for other novices, users of average knowledge and even teachers.  This in my opinion is tragic, for several reasons:
    • Naked pointers are a conceptual requirement to understand smart pointers, however, novices should avoid them at all costs until they have become familiar with at least the three most important classes of smart pointers:
      1. unique pointers,
      2. reference counted,
      3. and intrusive.
      But this does not happen in practice. The use of naked pointers leads to the full complexity and propensity for errors of managing memory explicitly, which is a difficult subject at all levels of expertise.  When novices, and also mid-level programmers use naked pointers, chances are they will mismanage them, because they don't have a complete understanding of what they are doing, corrupting the memory and having programs that crash and misbehave in subtle ways.  As soon as they being using arrays, dynamic memory, or any of the other things that naturally lead to pointers in C++, and the problems surface, they will get frustrated, the experience will be very unrewarding.
    • What applies to what is easier, to teach up to naked pointers as opposed to go all the way into smart pointers, is also reflected in the deceptively simple notation for naked pointers and the tedious and repetitive notation for smart pointers.  They have great economy of syntax, despite being
      • extremely hard to use productively,
      • having only very narrow applicability,
      • extremely easy to misuse catastrophically
    • The greatest feature in all of this language, the implicit destructors that make the RAII (Resource Acquisition Is Initialization/returning or freeing a resource is its destruction) possible are not applicable to naked pointers.  That is, naked pointers lead away from one of the greatest things in C++
    • Smart pointers, seeing from the point of view of standardization, are unacceptably recent additions:
      1. unique_ptr, which can be a zero-performance-overhead smart pointer was added to the standard for '11, because it required move semantics to be added to the language itself, and then move semantics is a especially subtle concept.
      2. shared pointers (that is, reference counted) were added in the TR1.  Although the boost libraries have had a very good implementation of the shared_ptr, using them in practice used to have drawbacks:  The shared_ptr requires a lot of compiler template juggling, which used to make compilation much slower, when the programmer made a mistake, hard to decipher template errors, etc.
      3. With regards to intrusive pointers (pointers to objects that they themselves have the reference counter), unbelievably, there is no standard intrusive pointer;
      4. auto_ptr was never useful
    It is not my intention to unfairly criticize C++ for these naked/smart pointer problems, actually, the current conceptualization and implementation of smart pointers in C++ is nothing short of a major achievement of human civilization in my opinion; as many other bad things of C++, they arose simply because truly leading edge concepts, techniques were first possible in C++ and thus were not well understood when they first were implemented; later, the improvement in understanding coexisted with lots of necessarily imperfect usage of leading edge concepts and techniques that given C++'s community conservatism, needed to continue to be supported and made extremely hard to change anything.  Be it as it may, C++ still continues to evolve, robustly, continuing to be the first expression of leading edge concepts and techniques that we are bound to complain about in a decade's time for how much they are holding us back...
  • Almost everything is mutable by default, except compile-time constructs.  I think there shouldn't be a "const" keyword, I mean it should be implicit.  What should be made explicit is the mutability of named things.  Not just changing the contents of memory is just about the most expensive processor operation in terms of performance hit, but it is orders of magnitude harder to reason about mutable things than immutable ones.  That is, the compiler should have ways to express different degrees of immutability.  There has been progress in this area, for example, "constexpr", but much remains to be done.  This is a multifasceted topic that I'll probably cover over many articles, below you'll see two aspects in high detail: Aliasing and false sharing, that deal with mutability and the impossibility to express that things don't change.  There are languages in which mutability is avoided in extreme ways, those on the functional programming paradigm.  This is relevant to C++ in two ways:
    1. Metaprogramming is amazingly very much functional programming in concepts and practice, especially including the feature of immutability
    2. This language supports almost any sane approach to programming (except introspection, what is commonly referred to by the misnomer "reflection", because this requires substantial effort on the programmer to emulate), so the concepts can be applied. This is a digression, but there is a very interesting presentation by Sean Parent, "Inheritance Is The Base Class of Evil" on how to apply functional programming concepts and techniques in normal C++ that I hope I will treat at length some time.
  • On the other hand, and very paradoxically, compile time constructs are immutable.  I am not advocating compile time constructs should be any different, just calling the fact that meta programming in C++ does not resemble normal programming at all, the building blocks are fundamentally different.  This makes it hard even for experts to do metaprogramming, that is, even though it's possible to metaprogram in C++, which proves conclusively the richness and expressiveness of this language, it requires an altogether different set of skills.
  • To further complicate things, meta programming in C++ is based on a form of pattern matching that relies on the extremely subtle template specialization and especially confusing overload resolution mechanisms of C++; which effectively and thoroughly obfuscate the metaprogramming code.  Again, I don't want to unfairly criticize, if I thought C++'s current framework to express smart pointers is nothing short of a major accomplishment of human civilization, I am convinced beyond doubt C++ metaprogramming capabilities are.  C++ is so expressive that metaprogramming support, far from having been a design objective, was actually discovered into the language, invented and developed over 15 years.
  • Talking of template specializations and overload resolution, while extremely powerful features conceptually, their pathetic syntax leads to truly worrisome subtleties.  Determining which specialization is picked by the compiler, or which overload, is probably the hardest thing in programming C++.  It is not just hopelessly hard for automated tools, it is hard even for very experienced programmers!  I wonder how come this is so hard and the compilers don't give you any help; I mean that for example, compilers give you a preprocessing option that shows you the macros expanded, why wouldn't the compiler give you the equivalent code with the specializations and overloads selected?  We will talk more about this in the future.  See "noexcept" below.
  • Bears mentioning how hopeless it is to make C++ tools.  A code editor that will autocomplete a function call? allright, to begin with, is it a global or a member function? is there a template involved? are there specializations for that template?... to do it right, the tool must know what are the types of the arguments involved.  The problem is that to be able to do that, the tool must essentially parse 100% of the features of the language.  In due course I will collect apparently inoffensive code fragments in which it is hard to know what are the things involved.
  • "noexcept" and "constexpr" should be the default.  The "hottest" thing in C++ today I would think is "value semantics".  That you can express value semantics at all is proof of how expressive the language is, however, it gets tedious really quickly annotating yourself that things are const or constexpr and noexcept.  At least noexcept is a compile time operator that will tell you whether an expression can throw...
  • Passing by value, by pointer, const pointer, reference, const reference, and "rvalue reference" all have their subtleties, and in general the set is not useful.
    • In my opinion, the only justification for naked pointer arguments is to indicate optionality, the argument address may be null, in any case, the callee does not assume any ownership.
    • Without optionality, only pass by value or a form of reference
    • Returning naked pointers only makes sense for optional things not owned by the caller
    • For return value or arguments to a function use smart pointers only to indicate ownership.  While the current set of smart pointers allow to indicate the intended ownership, unfortunately it is not possible to dis-indicate optionality.  That's one reason why I think the standard library should have intrusive pointers, this allows to convert a value to a smart pointer.
    I wish there would exist a "truly const" modifier for arguments, which would mean this:  If the compiler determines it is performance-advantageous to copy the argument into a local value, that it is allowed to do so, because the argument truly won't change, that is, the compiler is also allowed to assume the location in which the value resides won't change so it can refer to it by its address.  It's time to go back to aliasing:
"Aliasing" means that the same thing is referred to as apparently different things.  First, an example that uses concrete types for ease of explanation:
void incrementAll(std::vector<BigThing> &bt, const BigThing &value)
{
    for(auto &v: bt) { v += value; }
}
By "BigThing" is meant some data type that is expensive to copy around.  For example, imagine an implementation of matrices as vectors of vectors.  It is easy to see that passing as parameter a const reference to a big thing is the right thing to do, or is it not?.
The issue is that in the simple code given, there is no guarantee that the value referenced through "value" does not lie within the vector, that "value" is not an alias to an element in the vector, or even, to parts of up to two elements of the vector.
Unless the function is inlined (in this case it is not, it probably should be), and also the compiler has full information about the arguments "bt" and "value" at the caller site, then it would not be able to prove that the location of "value" is not within the memory buffer of the vector "bt".  Unless the compiler seriously optimizes, it would not even be able to realize that the iteration of elements will happen over a sequential buffer, hence, it conceivably can insert code to check for the bounds of the buffer and determine whether the location of "value" may be within the bounds of the buffer for the vector, perform case switching, that is, to automatically transform the code into this:

template<
    typename V
> void vectorizeIncrement(V *buffer, const V *limit, const V *val);
    // do in parallel *ptr += *val
    // for ptr in the range [buffer, limit - sizeof(V)]
    // and assuming that *val does not change

void incrementAll(std::vector<BigThing> &bt, const BigThing &value) {
    auto vectorBuffer = bt.data();
    auto end = vectorBuffer + bt.size();
    auto valueLocation = &value;
    if(valueLocation < vectorBuffer or end < valueLocation) {
        vectorizeIncrement(vectorBuffer, end, valueLocation);
        // vectorizeIncrement would be something reasonable that attempts
        // to apply the increment to many elements in parallel
    } else { // "value" is going to be changed despite being "const"!
        // note that a "BigThing" at "valueLocation" may legitimately straddle
        // two "BigThing" elements in the vector...
        intptr_t
            valueLocationAsInteger = intptr_t(valueLocation),
            bufferAsInteger = intptr_t(vectorBuffer),
            diff = valueLocationAsInteger - bufferAsInteger,
            size = sizeof(BigThing),
            straddleBeginningIndex = diff / size,
            straddleEndingIndex = (diff + size - 1) / size; // integer ceiling
        vectorizeIncrement(
            vectorBuffer, vectorBuffer + straddleBeginningIndex, valueLocation
        );
        vectorBuffer[straddleBeginningIndex] += *valueLocation;
        if(straddleBeginningIndex != straddleEndingIndex) {
            vectorBuffer[straddleEndingIndex] += *valueLocation;
        }
        vectorizeIncrement(
            vectorBuffer + straddleEndingIndex + 1, end, valueLocation
        );
    }
}

The problem is that it is fundamentally impossible to know at compilation whether the extra work of doing the case analysis for whether the location of value is in the vector buffer, and this is still assuming that the compiler knows that the BigThing &operator+=(const BigThing &) does not have a side effect on either the vector or the value!!

Profile-guided optimization can certainly help, but even then, what's the guarantee that the profile cases will be representative of production use long after the code has been released?

Something as apparently as simple as telling the compiler "I am giving you the address only to prevent copying, but trust me, the contents do not change" is inexpressible in current C++.  "const" is only a restriction on your code, it means you prohibit yourself from changing the values declared const explicitly, but that says nothing about who else can change the values, such as a different thread, nor whether they can change implicitly, as a side effect of a function call.

Solving this goes way beyond incorporating "restrict", because unless inlined, any function call can have any arbitrary side effect; on the other hand, the only guaranteed solution to aliasing expressible within the current language, which is to pass by value and use local copies of values until the changes are written back at the end, is both a heavy handed semantic change and a performance pit.

The final remarks about these complications are:
  • Don't manage naked pointers, instead link your pointers to things that will delete them on their destructors, for example, smart pointers.
  • Take the trouble to constify and constexprify and noexceptify
  • Endure the pain of manually making your code less and less mutable, it pays off
  • When in doubt, err on the side of passing by value
  • Even if you are a not a sophisticate, learn enough of metaprogramming so that the compile time immutability tools help you accomplish guarantees of immutability in your practical code.