Formatting

Another wonderful boilerplate!

std::cout << "Some log: " << time << std::endl
          << "Some status: " << status << std::endl
          << "Some arbitrary info "
          << a << b << c << std::endl;

Quite standard C++. Although, I must admit, seeing C++ first time these double arrows we're looking cool. Like, you know, undisciplined kid with a mohawk and a “YOLO” tattoo on his forehead kinda cool.

Now this looks clunky, hard to maintain, has hard time reflecting the look of the message and not too reusable.

So, back in the day they had printf, eh?

printf("Some numbers a - %d b - %d c - %d",a,b,c);

Now that looks real nice. However, could it be more typesafe?

Rust dealt with this problem quite well in my opinion - best of the both worlds.

Here I'll offer some option for C++ that might be quite a good counter to the way too verbose usual C++ version and quite elegant but type safety lacking C version.

So, we can tackle this problem as long as we're… PACKING!

Templatious library has packs. What are packs? Packs are tuples on steroids.

int a,b,c;
a = 1; b = 2; c = 3;

auto p = SF::pack(a,b,c);

Above we instantiated a pack that holds references to three ints - a,b,c.

The default behaviour of a pack is that it saves references when it can and copies when it must. In short, if we're packing a value that is on a stack (lvalue) pack stores an lvalue reference to it. However, if we pass rvalue reference to it then pack stores a copy made with rvalue constructor. More about this magic here if the previous sentence sounded like an exotic flavour of klingon.

So, what can we do with packs?

We can access elements one by one:

assert( &p.get<0>() == &a );

Great! We're in par with C++ tuples!

However, we can traverse packs…

auto f = [](int i) { std::cout << i << " "; };
SM::callEach(f,p);
// prints out "1 2 3"

Awesome! Boost fusion does that too.

So, why do we need packs?
Packs are composable…

auto p = SF::pack(a,b,c);
auto twoPack = SF::pack(p,p);
SM::callEach(f,twoPack);
// prints out "1 2 3 1 2 3"

Now we're talking… When we compose packs they are traversed recursively. All the functions to call are resolved at compile time, so, essentially, the above code block did the same and works as fast as the following:

f(a);
f(b);
f(c);
f(a);
f(b);
f(c);

So, let's use these mechanics to eliminate the nasty arrow operator for good.

However, we might need some small utility to make the call of « operator convenient. There's a static method in StaticFactory called “streamOutFunctor”. If creates a functor which, when called, uses « operator on arbitrary type passed to it. Let's try it!

auto sf = SF::streamOutFunctor(std::cout);
// sf holds lvalue reference to std::cout.

sf(1,2,3);
// same as std::cout << 1 << 2 << 3;

Now, we can start packing… Let's replace the original boilerplate!

long time;
int status;
int a,b,c;

...

auto p = SF::pack(
    "Some log: ",time,"\n",
    "Some status: ",status,"\n",
    "Some arbitrary info ",a,b,c,"\n");
auto sf = SF::streamOutFunctor(std::cout);
SM::callEach(sf,p);

And that looks quite shorter. It's arguable if it's more readable than the nicely readable printf version but it's something to consider.

Now, since pack saves references we also can mutate any of the used variables in pack and just call stream functor on all of them to print them again.

time = 7;
SM::callEach(sf,p);
status = 17;
SM::callEach(sf,p);
// and so on

However, the more we pack into one pack the less reusable it is. Can we split packs by lines?

auto topLine = SF::pack("Some log: ",time);
auto midLine = SF::pack("Some status: ",status);
auto botLine = SF::pack("Some arbitrary info: ",
    a,b,c);

auto full = SF::pack(
    topLine,"\n",midLine,"\n",botLine,"\n");
SM::callEach(sf,full);

Works just as before, however, log messages are chunked. And the more chunked things are the more reusable they are.

Now, that is kinda nice, but do we have to repeat the line separators? Nope, we don't. We can simply create a pack out of existing pack by inserting elements in between with packInsert method.

auto full = SF::packInsert(
    SF::pack(topLine,midLine,botLine),
    "\n");

And, also, bot line crams three numbers together so we can fix that also:

auto botLine = SF::pack("Some arbitrary info: ",
    SF::packInsert(SF::pack(a,b,c)," "));

now numbers are separated and will print with gaps.

Now, we can go completely nuts with this, only limited by our compiler and our imagination:

std::string x,y;
x = "female";
y = "male";

auto words = SF::pack(
   "Have","ye","not","read","that","he","which",
   "made","them","at","the","beginning","made",
   "them",y,"and",x);
auto sentence =
    SF::pack(SF::packInsert(words,'^'),'?');

auto numbers = SF::pack(1,2,3,4,5,6,7);
auto calc = SF::packInsert(numbers," + ");
auto answer = SF::pack(calc," = ",
    SM::sum<int>(numbers));
auto gap = SF::packRepeat<15>(' ');
auto leftInset = SF::pack(
    gap,
    SF::packRepeat<7>('>'));
auto rightInset = SF::pack(
    SF::packRepeat<7>('<'),
    gap);

auto splitter = SF::packRepeat<28>("#%&");

auto formAnswer = SF::packInsert(
    SF::pack(leftInset,answer,rightInset),' ');
auto msg = SF::pack(splitter,
    formAnswer,sentence,
    formAnswer,splitter);
auto finalS = SF::pack(
    SF::packInsert(msg,"\n"),"\n");

SM::callEach(sf,finalS);

y = "man";
x = "woman";

SM::callEach(sf,finalS);

All for now folks!