Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Global lambdas and ODR

230 views
Skip to first unread message

raskolnikov

unread,
Apr 7, 2016, 9:00:13 PM4/7/16
to
{ 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

unread,
Apr 11, 2016, 8:40:14 PM4/11/16
to

On Thursday, April 7, 2016 at 8:00:13 PM UTC-5, raskolnikov wrote:
> { 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!

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

Juan Pedro Bolivar Puente

unread,
Apr 12, 2016, 7:50:09 AM4/12/16
to

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

Juan Pedro Bolivar Puente

unread,
Apr 12, 2016, 7:50:12 AM4/12/16
to

Hi!

Thanks a lot for your response, it's inspiring...

> The difficult problem with lambdas is that they are not constexpr.

Agreed...

> 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.

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

pfultz2

unread,
Apr 12, 2016, 2:00:17 PM4/12/16
to
{ 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
wrote:
> Hi!
>
> Thanks a lot for your response, it's inspiring...
>
> > The difficult problem with lambdas is that they are not constexpr.
>
> Agreed...
>
> > 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.
>
> 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.

> 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>(...);

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);

>
> 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...

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

>
> 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
0 new messages