Adding to collection

Did you ever write…

std::vector<int> v;

v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
...
v.push_back(76);
v.push_back(77);

?

Okay, maybe you didn't write that many hardcoded push back statements. But, if from time to time you had to you'll be interested in this.

StaticAdapter has a method called add, it takes collection and elements you want to add.

std::vector<int> v;
SA::add(v,1,2,3,4,..,76,77);

Now, by factoring out addition to collection into generic static function we get some advantages:

  • Addition to collection is type agnostic. Anything that specializes CollectionAdapter class in templatious can be added to.
  • Programmer has to remember only one function to add to collection and is not forced to open documentation (especially crucial if development enviroment doesn't support semantic autocompletion listing members of collection).
  • Since these are templates and there is no polymorphic wrapping 99% of the cases compiler will optimize this out to the speed had you just written the boilerplate.

This approach is certainly more elegant but still not good enough. I mean, adding that many numbers in a row is a pain anyway. In this current instance we are adding numbers from 1 to 77. Templatious library has sequence class which can help us here.

std::vector<int> v;

SA::add(v,SF::seqI(1,77));

Tadaaa! Now, is this magic, we were just adding values one by one and we can just pop the whole sequence into collection like that? Actually, we can pop into collection anything that specializes CollectionAdapter class. Sequence in templatious is just another collection after all, except it cannot be mutated and compiler will certainly complain if you try to add to it.

Of course, if you try to pop a vector of strings into vector of ints you may get a nasty compile time error. Sometimes VERY nasty compile time error.

Let's try it, just for the heck of it!

std::vector<int> v;

SA::add(v,"yo slick!");

The static assert is triggered which says “Type could not be added to collection.” - this means that type could not be added to collection.

Here we are lucky - error is somewhat humane and merciful. However, from time to time of using the library you'll get the dreaded, legendary, myth-wrapped gigantic template error messages.

If you happen to be that lucky, here's the steps you should take:

  • Breathe deeply.
  • Consult documentation and review the arguments you passed into some templatious function to make sure they are correct.
  • If you're pretty sure that code should compile but it doesn't submit an issue into github to pass your pain to another developer and in the meantime write boilerplate instead.

Back to the addition, we can add another thing to the collection called pack. In templatious packs are tuples on steroids. They can be composed of any types and take references when they can and copies when they must. Let's try it out:

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

// make the pack!
auto p = SF::pack(a,b,c);

std::vector<int> v1;
std::vector<int> v2;

SA::add(v1,p);
// v1 = [1,2,3]
b = 7;
SA::add(v2,p);
// v2 = [1,7,3];
// packs save references of values
// by default.

Neat! We just made pack of arbitrary values and reused it to add them to different collections.

HOW ABOUT MOAR?

Packs can save arbitrary types of variables… You mean, we will always be that lucky just to hit ints when we need ints and hit doubles when we need doubles? Don't think so.

There's a special overload called addCustom which passes a variable through a specified function before adding it to collection. We will reuse our conversion to double match functor created back in the day to transform arbitrary values to double. LET'S BALL!

static auto toDouble = /* MAGIC */;

auto p = SF::pack(7.6,"7.7",std::string("7.8"));
std::vector<double> v;
SA::addCustom(v,toDouble,p);
// v = [7.6,7.7,7.8]

BOOM, PERFECT!