Wrong bound type in BindFirst

Slav Grig
2009-04-07
2013-04-08
  • Slav Grig
    Slav Grig
    2009-04-07

    When Andrei wrote his first version of the Loki, he made some things simple. I mean, for example, this code:

        class BinderFirst
            : public Private::BinderFirstTraits<OriginalFunctor>::Impl
        {
       
            ...

            typedef typename OriginalFunctor::Parm1 BoundType;

            ...

            BinderFirst(const OriginalFunctor& fun, BoundType bound)
            : f_(fun), b_(bound)
            {}
           
            ...
           
            BoundType b_;
        };

    The binder object stores the copy of the first parameter. There is no dependence on the parameter type. Today library have more sophisticated code, which tries to optimize the parameter storage based on its type. General objects will be stored as references. And next code will not work with new library:

    void funcTest(std::string s)
    {
        std::cout << s;
    }

    ::Loki::Functor<void, ::Loki::NullType> setter()
    {
        std::string s("some string");
        return ::Loki::BindFirst(
            ::Loki::Functor<void, LOKI_TYPELIST_1(std::string)>(&funcTest),
            s);
    }

    int main(int argc, char *argv[])
    {
        ::Loki::Functor<void, ::Loki::NullType> f1;
        f1 = setter();
        f1();
        return 0;
    }

    New binder object is created in the "setter" function. Unfortunately it tries to save parameter reference inside himself. And this reference becomes invalid after exiting from the "setter" function. So, I got a crash on activating the functor.

    I think, there is no need for parameter storage optimization in the binder object. As we know, the goal of a functor is to separate a time of a call initialization and a time of it activation. And nobody knows what parameters state is at the moment of activation a functor.

    If somebody wants to optimize parameters storage and may guarantee that parameters still alive, he(she) may write, for example:

    std::string global_s("some string");

    ::Loki::Functor<void, ::Loki::NullType> setter()
    {
        return ::Loki::BindFirst(
            ::Loki::Functor<void, LOKI_TYPELIST_1(std::string&)>(&funcTest),
            global_s);
    }

    Maybe Andrei was right?

     
    • Peter Kuemmel
      Peter Kuemmel
      2009-04-07

      Thanks for cachting this!

      The change was committed by me:
      "speed optimization: pass by reference but store value in case of Functor parameters"
      http://sourceforge.net/forum/forum.php?thread_id=1403779&forum_id=93009

      Maybe it is better to always store the value not only in case of Functor.

      Peter

       
    • Peter Kuemmel
      Peter Kuemmel
      2009-04-07

    • Slav Grig
      Slav Grig
      2009-04-08

      I think, in case of a functor, it's good to use Private::BinderFirstTraits<OriginalFunctor>::OriginalParm1 as bound type storage. If somebody wants to put objects as parameters, copies of objects will be stored in the binder object. But if somebody wants to put references (for example, if the functor invocation must change some global object), this case should be specified in the functor typelist.

      So, my variant of BoundTypeStorage type definition is:

      typedef typename Private::BinderFirstTraits<OriginalFunctor>::OriginalParm1 BoundTypeStorage;

      All other things are the same. And all examples in the first post are working!