• Global lambdas and ODR

    From raskolnikov@21:1/5 to All on Thu Apr 7 19:56:51 2016
    { 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! ]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pfultz2@21:1/5 to raskolnikov on Mon Apr 11 19:30:04 2016
    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


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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Juan Pedro Bolivar Puente@21:1/5 to All on Tue Apr 12 06:49:34 2016
    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! ]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From pfultz2@21:1/5 to All on Tue Apr 12 12:53:18 2016
    { 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

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)