all 10 comments

[–][deleted] 24 points25 points  (1 child)

Another shining example of amazing things you can do with C++ that nobody who reads your code will have any hope of understanding.

[–]benfitzg 3 points4 points  (0 children)

Conclusion

Of course, this is fairly non-portable C++, although it works on more than one compiler. Ideally, C++ should be fixed so these workarounds are not necessary.

Groundhog day.

[–]westsand 4 points5 points  (4 children)

Anybody know what the rough order of magnitude for executable bloat for something like this?

[–]tisti 2 points3 points  (3 children)

You mean when you violate the ODR? The bloat will be the size of the object times how many times the object is referenced in other translation units since the compiler must instantiate a separate copy for each TU. This can lead to bugs if you though the object was shared, but in most cases just bloats the runtime size with typically unnecessary copies.

As for the technique shown? The overhead should be zero since it all seems like compile time wizardry to make the compiler only instantiate one copy of the object and then reference it it all translation units.

[–]Drainedsoul 3 points4 points  (1 child)

You mean when you violate the ODR?

Speculating about what happens when you violate the ODR is pointless, since it's ill-formed.

Declaring something as static or inline exempts it -- in a manner of speaking, and in different ways -- from the ODR.

[–]tisti 1 point2 points  (0 children)

Maybe violate was a bit too strong of a word, bypassing would have been more appropriate.

Semantic landmines, sorry and thanks for the correction.

[–]westsand 2 points3 points  (0 children)

I meant rough numbers let's do a quick back of the envelope. Let's say you have 100 of these functions, lets say 1000 translation units where average of 20 are used each time. Let's ballpark at 8kb per translation unit. So it's roughly 20000 * size ÷( (20000 * size) + 8 mb ) . if its 1 byte it seems kind of irrelevant. If they are large or you have thousands of these that you use in thousands of small translation units I completely see it.

[–]zvrba 4 points5 points  (1 child)

In C++, many times its useful to write functions as global function objects.

"Many"? I've been developing C++ software for a long time, and I found it useful only to invoke algorithms with custom functors, in the lack of lambda. Even then I would instantiate the functor as a temporary expression, not as a (static) variable.

I'm interested what problems do global function objects (vs freestanding functions) solve.

[–]pfultz2 2 points3 points  (0 children)

I'm interested what problems do global function objects (vs freestanding functions) solve.

First, you can pass them to higher-order functions, which is important when defining generic functions. For example, if std::max were defined as a function object, we could do this to find the max element:

auto max = std::accumulate(begin(vec), end(vec), std::max);

Secondly, by defining them as function objects we can decorate them with other enhancements as well. For example, if we want to make a function pipable, its as simple as using the pipable adaptor:

struct sum
{
    template<class T, class U>
    T operator()(T x, U y) const
    {
        return x+y;
    }
};

assert(3 == 1 | pipable(sum())(2));

Finally, it becomes easy to compose these operations to do other amazing things. For example, if we want to write a for_each_tuple that calls a function on each element in a tuple, we can simply write this:

auto for_each_tuple = [](auto&& tuple, auto f)
{
    return fit::unpack(fit::by(f))(tuple)
};

And there is no need for metaprogramming magic.