Date Tags cpp

I would like to highlight a technique in C++ called mixin layers and present a use case, where it is very useful.

Mixin layers is a template-based design, where the layers are used a building blocks to quickly construct applications with reusable code. My favourite example is a networking application, where headers constructed/are added at the sender side, and parsed/removed.

In the application code, it would look something like this:

mixin.cpp download
#include <iostream>
#include <cstring>

#include "header_seq.hpp"
#include "header_id.hpp"
#include "sock_udp.hpp"
#include "buffer.hpp"
#include "final_layer.hpp"

// define the stack by listing the needed layers
typedef header_seq<
        header_id<
        sock_udp<
        buffer<
        final_layer
        >>>> net_stack;

int main(int argc, char **argv)
{
    net_stack ns;
    net_stack::buffer_type buf;

    if (argc < 2) {
        std::cerr << "missing recv/send argument" << std::endl;
        return 1;
    }

    if (strncmp(argv[1], "send", 5) == 0) {
        // sender side
        std::cout << "id:  " << ns.id_out() << std::endl;
        std::cout << "seq: " << ns.seq_out() << std::endl;
        ns.send_buf(buf);
    } else if (strncmp(argv[1], "recv", 5) == 0) {
        // receiver side
        ns.recv_buf(buf);
        std::cout << "id:  " << ns.id_in() << std::endl;
        std::cout << "seq: " << ns.seq_in() << std::endl;
    } else {
        std::cerr << "invalid recv/send argument" << std::endl;
        return 1;
    }

    return 0;
}

To use the layers, one simply includes the needed headers, and create a #stack# with the typedef shown above. The stack then defines two primary functions to be used: send_buf() and recv_buf().

When calling those functions, each layer intercepts the call, and does whatever it has to do with the buffer, before forwarding the call to the next layer. In our example, the first layer to intercept send_buf() is header_seq, which looks like this:

header_seq.hpp download
#pragma once

#include <endian.h>

template<class super>
class header_seq : public super
{
    typedef typename super::buffer_type buffer;
    typedef uint32_t seq_type;

    struct header_seq_type
    {
        seq_type seq;
    };

    constexpr static size_t m_header_seq_len = sizeof(struct header_seq_type);
    seq_type m_seq_out = 0;
    seq_type m_seq_in = 0;

    header_seq_type *cast_header(buffer &buf)
    {
        return reinterpret_cast<header_seq_type *>(buf.head());
    }

    void write_seq(buffer &buf)
    {
        buf.head_add(m_header_seq_len);
        cast_header(buf)->seq = htobe32(m_seq_out);
    }

    void read_seq(buffer &buf)
    {
        m_seq_in = be32toh(cast_header(buf)->seq);
        buf.head_del(m_header_seq_len);
    }

  public:
    // read sequence header from buffer and forward the buffer up
    bool recv_buf(buffer &buf)
    {
        if (!super::recv_buf(buf))
            return false;

        read_seq(buf);

        return true;
    }

    // add sequence header to buffer and forward the buffer down
    bool send_buf(buffer &buf)
    {
        write_seq(buf);

        if (!super::send_buf(buf))
            return false;

        ++m_seq_out;

        return true;
    }

    seq_type seq_out() const
    {
        return m_seq_out;
    }

    seq_type seq_in() const
    {
        return m_seq_in;
    }
};

The class hooks into the stack by inheriting from the template class super. By doing this, it "his" send_buf() declared in lower layers.

The mixin technique becomes useful when maximum flexibility is needed. Like when I build prototypes for research: I can simply build up a network application for testing a protocol by plugging a bunch of layers together.

The above source code is from an example application I wrote for this blog post. You can see the entire source code here. Compile it like this:

clang++ -std=c++11 mixin.cpp -o mixin