Discussion:
Global lambdas and ODR
(too old to reply)
raskolnikov
2016-04-08 01:56:51 UTC
Permalink
{ edited by mod to shorten lines to ~70 characters. -mod }

Hi!

I was thinking about what some people including I are trying with
transducers in C++14, which involves defining global lambdas [1] [2].

However, I am becoming afraid that this in fact violates the One
Definition Rule. Not only is the object defined in multiple translation
units, it is even defined with different types!

I'd imagine that one workaround would be to wrap the the lambda in
a factory
function template.

template <typename T=int>
auto lambdaz() { return [] { ... } };

And use like lambdaz(). This would work for most transducers since the
outermost lambda layer can be turned into a normal function with minor
implications for clients.

Another option would be to use a variable template, something
like:

template <typename T=int>
auto lambdaz = [] { ... };

But then use would require to type lambdaz<> with those weird angles
that make no sense for the client.

Any ideas on how can this be solved simpler?

Thanks!

JP

[1]
https://github.com/Ableton/atria/blob/master/src/atria/xform/transducer/
map.hpp
[2]
https://github.com/kirkshoop/transducer/blob/master/src/ducer/ducer_mapp
er.h
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
pfultz2
2016-04-12 01:30:04 UTC
Permalink
Post by raskolnikov
{ edited by mod to shorten lines to ~70 characters. -mod }
Hi!
I was thinking about what some people including I are trying with
transducers in C++14, which involves defining global lambdas [1] [2].
However, I am becoming afraid that this in fact violates the One
Definition Rule. Not only is the object defined in multiple
translation
Post by raskolnikov
units, it is even defined with different types!
You may want to look at this, which discusses these issues:

http://pfultz2.com/blog/2015/05/31/unique-address/

The difficult problem with lambdas is that they are not constexpr.
Otherwise,
you could just write:

template<class T>
constexpr auto global = T{};

template<class T>
const auto& static_const_var(const T&)
{
return global<T>;
}

constexpr auto&& lambda = static_const_var([]{ ... });

In the Fit library[1], I would like to support the factory pattern for
lambdas, so you could write this:

auto lambda_factor()
{
return []{ ... };
}

FIT_STATIC_FUNCTION(lambda) = fit::indirect(&lambda_factor, fit::apply);

The fit::indirect adaptor needs to be extended to support a custom
operator.

I don't know if any of those patterns will help you with your
transducers.

Paul

[1]: https://github.com/pfultz2/Fit
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Juan Pedro Bolivar Puente
2016-04-12 12:49:34 UTC
Permalink
Hi all,

I just found this other article that adds more information and tricks to
the topic:

http://pfultz2.com/blog/2015/05/31/unique-address/

Cheers!

JP
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Juan Pedro Bolivar Puente
2016-04-12 12:49:29 UTC
Permalink
Hi!

Thanks a lot for your response, it's inspiring...
Post by pfultz2
The difficult problem with lambdas is that they are not constexpr.
Agreed...
Post by pfultz2
In the Fit library[1], I would like to support the factory pattern for
auto lambda_factor()
{
return []{ ... };
}
FIT_STATIC_FUNCTION(lambda) = fit::indirect(&lambda_factor, fit::apply);
The fit::indirect adaptor needs to be extended to support a custom
operator.
That sounds interesting, but if I am understanding this correctly after
looking at the Fit code, this will call `lambda_factor` via a function
pointer whenever we want to call `lambda`. While there is some chance
that it will still be inlined, I don't totally like it. I guess an
alternative, but again more boilerplaty, would be to write something like:

struct lambda_factor
{
auto operator()
{
return [] { ... };
}
};

FIT_STATIC_FUNCTION(lambda) = fit::indirect2<lambda_factor>(...);

All this could maybe be wrapped in a macro such one could write
something like:

FIT_GLOBAL_LAMBDA(lambda, ([] {
...
}));

but admittedly it does not feel very nice...

I hope the standard eventually tackles the problem, ideally by making
lambdas constexpr if needed, but I do understand that the problem is
hard for the C++ compilation model...

Thanks!

JP
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
pfultz2
2016-04-12 18:53:18 UTC
Permalink
{ edited by mod: quoted signature and server banner redacted. -mod }

On Tuesday, April 12, 2016 at 6:50:12 AM UTC-5, Juan Pedro Bolivar Puente
Post by Juan Pedro Bolivar Puente
Hi!
Thanks a lot for your response, it's inspiring...
Post by pfultz2
The difficult problem with lambdas is that they are not constexpr.
Agreed...
Post by pfultz2
In the Fit library[1], I would like to support the factory pattern for
auto lambda_factor()
{
return []{ ... };
}
FIT_STATIC_FUNCTION(lambda) = fit::indirect(&lambda_factor, fit::apply);
The fit::indirect adaptor needs to be extended to support a custom
operator.
That sounds interesting, but if I am understanding this correctly after
looking at the Fit code, this will call `lambda_factor` via a function
pointer whenever we want to call `lambda`. While there is some chance
that it will still be inlined, I don't totally like it.
The function pointer is initialized at compile-time, so it is very likely to
be inlined.
Post by Juan Pedro Bolivar Puente
I guess an
struct lambda_factor
{
auto operator()
{
return [] { ... };
}
};
FIT_STATIC_FUNCTION(lambda) = fit::indirect2<lambda_factor>(...);
Yes a function object could be used as well. I don't know what the
`indirect2`
does. It could be written like this:

struct lambda_factor
{
auto operator()() const
{
return [] { ... };
}
};

FIT_STATIC_FUNCTION(lambda) = fit::indirect(lambda_factor{}, fit::apply);
Post by Juan Pedro Bolivar Puente
All this could maybe be wrapped in a macro such one could write
FIT_GLOBAL_LAMBDA(lambda, ([] {
...
}));
but admittedly it does not feel very nice...
The biggest problem with the macro like that is that the lambda is expanded
in
the macro itself which can make debugging problematic. In general, macros
should be used for things that are declarative. An alternative approach like
this could possible work, but I haven't tried it:

#define FIT_STATIC_FACTORY(name) \
struct name ## _factory { auto operator()() const; } \
FIT_STATIC_FUNCTION(name) = fit::indirect(name ## _factory{}, fit::apply); \
auto name ## _factory::operator()() const

So then you should be able to write:

FIT_STATIC_FACTORY(lambda)
{
return [] { ... };
}

Of course, fit::indirect doesn't support a custom operator yet, so until
then,
it could possibly be written like this using the dereference operator
instead:

#define FIT_STATIC_FACTORY(name) \
struct name ## _factory { auto operator*() const; } \
FIT_STATIC_FUNCTION(name) = fit::indirect(name ## _factory{}); \
auto name ## _factory::operator*() const
Post by Juan Pedro Bolivar Puente
I hope the standard eventually tackles the problem, ideally by making
lambdas constexpr if needed, but I do understand that the problem is
hard for the C++ compilation model...
I believe C++17 is on track for constexpr lambdas which would simplify part
of
it. The other part is to support inline variables so the dance with template
variables or static class variables is unnecessary, but I don't know how far
the committee has gotten with that. Hopefully, in C++17 we will be able to
just write:

extern constexpr auto lambda = [] { ... };

And thats it.

Paul
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Loading...