Pimpl idiom without dynamic memory allocation

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Daniel Lidström

    #1

    Pimpl idiom without dynamic memory allocation

    Hello!

    I have just discovered a way to use the private implementation idiom
    (pimpl), without the overhead of dynamic memory allocation. For those of
    you who don't know what this is, Wikipedia has a nice article you can
    read. Anyway, I discovered that if you make all members in the
    implementation class mutable, you can in fact use this idiom without any
    "unnecessar y" memory allocation. Here's a minimal example of the method:

    // In the header of your class called Line

    #include <string>

    class Line
    {
    public:

    Line(const std::string& name);
    const std::string& GetName() const;
    void SetName(const std::string& s);

    private:

    // Private implementation idiom:
    // all member variables are hidden in this class
    class LineImpl;
    const LineImpl& m_pimpl; // normally a non-const pointer
    };

    // and in your implementation file:

    #include "Line.h"

    // Here we define the class with the member variables
    class Line::LineImpl
    {
    public:

    LineImpl(const std::string& s) : m_s(s) {}
    // all methods need to be const here
    const std::string& GetName() const { return m_s; }
    void SetName(const std::string& s) const { m_s = s; }

    private:

    mutable std::string m_s; // the trick! all members are mutable
    };

    // create the pimpl instance without using new
    Line::Line(cons t std::string& s) : m_pimpl(LineImp l(s)) {}

    // forward all member functions to the private implementation
    const std::string& Line::GetName() const
    {
    return m_pimpl.GetName ();
    }

    void Line::SetName(c onst std::string& s)
    {
    m_pimpl.SetName (s);
    }

    Ok experts, what do you all think? This method sacrifies
    const-correctness for some extra speed. Is it worth it?

    --
    Daniel
  • Michael DOUBEZ

    #2
    Re: Pimpl idiom without dynamic memory allocation

    Daniel Lidström a écrit :
    Hello!
    >
    I have just discovered a way to use the private implementation idiom
    (pimpl), without the overhead of dynamic memory allocation. For those of
    you who don't know what this is, Wikipedia has a nice article you can
    read. Anyway, I discovered that if you make all members in the
    implementation class mutable, you can in fact use this idiom without any
    "unnecessar y" memory allocation. Here's a minimal example of the method:
    >
    // In the header of your class called Line
    >
    #include <string>
    >
    class Line
    {
    public:
    >
    Line(const std::string& name);
    const std::string& GetName() const;
    void SetName(const std::string& s);
    >
    private:
    >
    // Private implementation idiom:
    // all member variables are hidden in this class
    class LineImpl;
    const LineImpl& m_pimpl; // normally a non-const pointer
    };
    >
    // and in your implementation file:
    >
    #include "Line.h"
    >
    // Here we define the class with the member variables
    [snip]
    // all methods need to be const here
    [snip]
    mutable std::string m_s; // the trick! all members are mutable
    Which mean you coerce the code into compilation. That's all.
    // create the pimpl instance without using new
    Line::Line(cons t std::string& s) : m_pimpl(LineImp l(s)) {}
    Your local is destroyed when going out of scope. Doesn't it ?
    [snip]
    Ok experts, what do you all think? This method sacrifies
    const-correctness for some extra speed. Is it worth it?
    Not really.
    And certainly not worth a dangling reference.


    Michael

    Comment

    • Daniel Lidström

      #3
      Re: Pimpl idiom without dynamic memory allocation

      In article <4716585e$0$250 87$426a74cc@new s.free.fr>,
      Michael DOUBEZ <michael.doubez @free.frwrote:
      Daniel Lidström a écrit :
      // create the pimpl instance without using new
      Line::Line(cons t std::string& s) : m_pimpl(LineImp l(s)) {}
      >
      Your local is destroyed when going out of scope. Doesn't it ?
      No it isn't. It is actually ok to bind a temporary object to a const
      reference. There will be no "dangling" reference.

      --
      Daniel

      Comment

      • Michael DOUBEZ

        #4
        Re: Pimpl idiom without dynamic memory allocation

        Daniel Lidström a écrit :
        In article <4716585e$0$250 87$426a74cc@new s.free.fr>,
        Michael DOUBEZ <michael.doubez @free.frwrote:
        >
        >Daniel Lidström a écrit :
        >>// create the pimpl instance without using new
        >>Line::Line(co nst std::string& s) : m_pimpl(LineImp l(s)) {}
        >Your local is destroyed when going out of scope. Doesn't it ?
        >
        No it isn't. It is actually ok to bind a temporary object to a const
        reference. There will be no "dangling" reference.
        It is ok to bind it but that doesn't mean the lifetime of the temporary
        is extended.

        Example:
        const std::string& foo()
        {
        return std::string("ba r");
        }

        The value returned by foo() is an dangling reference.

        Michael

        Comment

        • Daniel T.

          #5
          Re: Pimpl idiom without dynamic memory allocation

          Daniel Lidström <somebody@micro soft.comwrote:
          // In the header of your class called Line
          >
          #include <string>
          >
          class Line
          {
          public:
          >
          Line(const std::string& name);
          const std::string& GetName() const;
          void SetName(const std::string& s);
          >
          private:
          >
          // Private implementation idiom:
          // all member variables are hidden in this class
          class LineImpl;
          const LineImpl& m_pimpl; // normally a non-const pointer
          };
          >
          // and in your implementation file:
          >
          #include "Line.h"
          >
          // Here we define the class with the member variables
          class Line::LineImpl
          {
          public:
          >
          LineImpl(const std::string& s) : m_s(s) {}
          // all methods need to be const here
          const std::string& GetName() const { return m_s; }
          void SetName(const std::string& s) const { m_s = s; }
          >
          private:
          >
          mutable std::string m_s; // the trick! all members are mutable
          };
          >
          // create the pimpl instance without using new
          Line::Line(cons t std::string& s) : m_pimpl(LineImp l(s)) {}
          Where would the memory for the LineImpl object be placed? It isn't
          embedded in the object, nor is it in the heap, and it can't be placed on
          the stack (and still survive the call to the c_tor.)

          Doesn't sound like a good idea to me.

          Comment

          • Ulrich Eckhardt

            #6
            Re: Pimpl idiom without dynamic memory allocation

            Daniel Lidström wrote:
            I have just discovered a way to use the private implementation idiom
            (pimpl), without the overhead of dynamic memory allocation.
            [ storing a reference to a temporary ]

            As you noticed, this doesn't work. However, there is a method that works.
            All you have to do is to add a suitably aligned and sufficiently large
            buffer into the class:

            class foo {
            aligned_storage <42m_impl;
            class implementation;
            foo();
            ~foo();
            void some_function() ;
            };

            class foo::implementa tion { ... };

            foo::foo() {
            // placement new
            new m_impl.get<void >() implementation;
            }
            foo::~foo() {
            // explicit dtor invokation
            m_impl.get<impl ementation>()->~implementatio n;
            }
            void foo::some_funct ion() {
            m_impl.get<impl ementation>()->some_function( );
            }

            Is it worth the hassle? Typically not, in particular since it's hard to
            guarantee that you have both enough but still not too much memory.

            Uli

            Comment

            • James Kanze

              #7
              Re: Pimpl idiom without dynamic memory allocation

              On Oct 17, 10:36 pm, "Daniel T." <danie...@earth link.netwrote:
              Daniel Lidström <someb...@micro soft.comwrote:
              // In the header of your class called Line
              #include <string>
              class Line
              {
              public:
              Line(const std::string& name);
              const std::string& GetName() const;
              void SetName(const std::string& s);
              private:
              // Private implementation idiom:
              // all member variables are hidden in this class
              class LineImpl;
              const LineImpl& m_pimpl; // normally a non-const pointer
              };
              // and in your implementation file:
              #include "Line.h"
              // Here we define the class with the member variables
              class Line::LineImpl
              {
              public:
              LineImpl(const std::string& s) : m_s(s) {}
              // all methods need to be const here
              const std::string& GetName() const { return m_s; }
              void SetName(const std::string& s) const { m_s = s; }
              private:
              mutable std::string m_s; // the trick! all members are mutable
              };
              // create the pimpl instance without using new
              Line::Line(cons t std::string& s) : m_pimpl(LineImp l(s)) {}
              Where would the memory for the LineImpl object be placed? It
              isn't embedded in the object, nor is it in the heap, and it
              can't be placed on the stack (and still survive the call to
              the c_tor.)
              >From the standard (§12.2/5): "A temporary bound to a reference
              member in a constructor's ctor-initializer persists until the
              constructor exits." In colloquial terms: the temporary is
              created on the stack, and destructed before returning from the
              constructor.
              Doesn't sound like a good idea to me.
              It isn't, unless you like undefined behavior and hard to find
              bugs.

              --
              James Kanze (GABI Software) email:james.kan ze@gmail.com
              Conseils en informatique orientée objet/
              Beratung in objektorientier ter Datenverarbeitu ng
              9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


              Comment

              • Yannick Tremblay

                #8
                Re: Pimpl idiom without dynamic memory allocation

                In article <47165e09$0$267 28$426a74cc@new s.free.fr>,
                Michael DOUBEZ <michael.doubez @free.frwrote:
                >Daniel Lidström a écrit :
                >In article <4716585e$0$250 87$426a74cc@new s.free.fr>,
                > Michael DOUBEZ <michael.doubez @free.frwrote:
                >>
                >>Daniel Lidström a écrit :
                >>>// create the pimpl instance without using new
                >>>Line::Line(c onst std::string& s) : m_pimpl(LineImp l(s)) {}
                >>Your local is destroyed when going out of scope. Doesn't it ?
                >>
                >No it isn't. It is actually ok to bind a temporary object to a const
                >reference. There will be no "dangling" reference.
                >
                >It is ok to bind it but that doesn't mean the lifetime of the temporary
                >is extended.
                >
                >Example:
                >const std::string& foo()
                >{
                return std::string("ba r");
                >}
                >
                >The value returned by foo() is an dangling reference.
                >
                Euh, it's not what he is doing, it's more like:

                std::string foo()
                {
                return std::string("ba r");
                }

                int main()
                {
                std::string const & val = foo();
                ....


                Comment

                • Kai-Uwe Bux

                  #9
                  Re: Pimpl idiom without dynamic memory allocation

                  Yannick Tremblay wrote:
                  In article <47165e09$0$267 28$426a74cc@new s.free.fr>,
                  Michael DOUBEZ <michael.doubez @free.frwrote:
                  >>Daniel Lidström a écrit :
                  >>In article <4716585e$0$250 87$426a74cc@new s.free.fr>,
                  >> Michael DOUBEZ <michael.doubez @free.frwrote:
                  >>>
                  >>>Daniel Lidström a écrit :
                  >>>>// create the pimpl instance without using new
                  >>>>Line::Line( const std::string& s) : m_pimpl(LineImp l(s)) {}
                  >>>Your local is destroyed when going out of scope. Doesn't it ?
                  >>>
                  >>No it isn't. It is actually ok to bind a temporary object to a const
                  >>reference. There will be no "dangling" reference.
                  >>
                  >>It is ok to bind it but that doesn't mean the lifetime of the temporary
                  >>is extended.
                  >>
                  >>Example:
                  >>const std::string& foo()
                  >>{
                  > return std::string("ba r");
                  >>}
                  >>
                  >>The value returned by foo() is an dangling reference.
                  >>
                  >
                  Euh, it's not what he is doing, it's more like:
                  >
                  std::string foo()
                  {
                  return std::string("ba r");
                  }
                  >
                  int main()
                  {
                  std::string const & val = foo();
                  ...
                  It's neither. What he is doing is initialzing a reference member from a
                  temporary object. The lifetime of that object lasts exactly to the end of
                  the constructor call. Afterwards, i.e., for the entire lifetime of the
                  fully constructed object, the reference is dangling. From the standard:

                  [...] A temporary bound to a reference member in a constructor?s
                  ctor-initializer (12.6.2) persists until the constructor exits. ...
                  [12.2/5]

                  This provision makes you wonder. What is the point of restricting the
                  life-time of the temporary to the duration of the constructor if the object
                  thus initialized is bound to have a dangling reference ever after?


                  Best

                  Kai-Uwe Bux

                  Comment

                  • Joe Greer

                    #10
                    Re: Pimpl idiom without dynamic memory allocation

                    Kai-Uwe Bux <jkherciueh@gmx .netwrote in
                    news:ff7r0f$rg6 $1@murdoch.acc. Virginia.EDU:
                    It's neither. What he is doing is initialzing a reference member from
                    a temporary object. The lifetime of that object lasts exactly to the
                    end of the constructor call. Afterwards, i.e., for the entire lifetime
                    of the fully constructed object, the reference is dangling. From the
                    standard:
                    >
                    [...] A temporary bound to a reference member in a constructor?s
                    ctor-initializer (12.6.2) persists until the constructor exits. ...
                    [12.2/5]
                    >
                    This provision makes you wonder. What is the point of restricting the
                    life-time of the temporary to the duration of the constructor if the
                    object thus initialized is bound to have a dangling reference ever
                    after?
                    >
                    It is interesting. I would have thought that it would last until the scope
                    in which the constructor was invoked exited and the stack space is
                    reclaimed. There is probably some case for creating the temporaries within
                    the scope of the constructor or something that I don't see at the moment.
                    In any case, I can't see this for the pimpl idiom.

                    joe

                    Comment

                    • Andre Kostur

                      #11
                      Re: Pimpl idiom without dynamic memory allocation

                      Kai-Uwe Bux <jkherciueh@gmx .netwrote in
                      news:ff7r0f$rg6 $1@murdoch.acc. Virginia.EDU:
                      Yannick Tremblay wrote:
                      >
                      >In article <47165e09$0$267 28$426a74cc@new s.free.fr>,
                      >Michael DOUBEZ <michael.doubez @free.frwrote:
                      >>>Daniel Lidström a écrit :
                      >>>In article <4716585e$0$250 87$426a74cc@new s.free.fr>,
                      >>> Michael DOUBEZ <michael.doubez @free.frwrote:
                      >>>>
                      >>>>Daniel Lidström a écrit :
                      >>>>>// create the pimpl instance without using new
                      >>>>>Line::Line (const std::string& s) : m_pimpl(LineImp l(s)) {}
                      >>>>Your local is destroyed when going out of scope. Doesn't it ?
                      >>>>
                      >>>No it isn't. It is actually ok to bind a temporary object to a
                      >>>const reference. There will be no "dangling" reference.
                      >>>
                      >>>It is ok to bind it but that doesn't mean the lifetime of the
                      >>>temporary is extended.
                      >>>
                      >>>Example:
                      >>>const std::string& foo()
                      >>>{
                      >> return std::string("ba r");
                      >>>}
                      >>>
                      >>>The value returned by foo() is an dangling reference.
                      >>>
                      >>
                      >Euh, it's not what he is doing, it's more like:
                      >>
                      >std::string foo()
                      >{
                      > return std::string("ba r");
                      >}
                      >>
                      >int main()
                      >{
                      > std::string const & val = foo();
                      >...
                      >
                      It's neither. What he is doing is initialzing a reference member from
                      a temporary object. The lifetime of that object lasts exactly to the
                      end of the constructor call. Afterwards, i.e., for the entire lifetime
                      of the fully constructed object, the reference is dangling. From the
                      standard:
                      >
                      [...] A temporary bound to a reference member in a constructor?s
                      ctor-initializer (12.6.2) persists until the constructor exits. ...
                      [12.2/5]
                      >
                      This provision makes you wonder. What is the point of restricting the
                      life-time of the temporary to the duration of the constructor if the
                      object thus initialized is bound to have a dangling reference ever
                      after?
                      I don't have the Standard handy... but does it change the details since
                      it's a const-reference?

                      Comment

                      • Joe Greer

                        #12
                        Re: Pimpl idiom without dynamic memory allocation

                        Andre Kostur <nntpspam@kostu r.netwrote in news:Xns99CD599 F98AC5nntpspamk osutrnet@
                        209.135.99.21:
                        >
                        I don't have the Standard handy... but does it change the details since
                        it's a const-reference?
                        >
                        As far as I can tell from the standard, it doesn't. In my own personal mind's eye of how things
                        work, I always imagine temporaries allocated on the stack and as soon as the appropriate stack
                        scope is gone, so is the temporary. I don't think that temporaries have to be on the stack, but
                        the implementations I have looked at act as though they are.

                        joe

                        Comment

                        • James Kanze

                          #13
                          Re: Pimpl idiom without dynamic memory allocation

                          On Oct 18, 4:35 pm, Kai-Uwe Bux <jkherci...@gmx .netwrote:
                          Yannick Tremblay wrote:
                          It's neither. What he is doing is initialzing a reference
                          member from a temporary object. The lifetime of that object
                          lasts exactly to the end of the constructor call. Afterwards,
                          i.e., for the entire lifetime of the fully constructed object,
                          the reference is dangling. From the standard:
                          [...] A temporary bound to a reference member in a constructor?s
                          ctor-initializer (12.6.2) persists until the constructor exits. ...
                          [12.2/5]
                          This provision makes you wonder. What is the point of
                          restricting the life-time of the temporary to the duration of
                          the constructor if the object thus initialized is bound to
                          have a dangling reference ever after?
                          Implementation constraints: where would the compiler put the
                          temporary if it were required to outlast the constructor? (I
                          suppose that the standard could have made it illegal to
                          initialize a reference member with a temporary.)

                          --
                          James Kanze (GABI Software) email:james.kan ze@gmail.com
                          Conseils en informatique orientée objet/
                          Beratung in objektorientier ter Datenverarbeitu ng
                          9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                          Comment

                          • James Kanze

                            #14
                            Re: Pimpl idiom without dynamic memory allocation

                            On Oct 18, 5:45 pm, Andre Kostur <nntps...@kostu r.netwrote:
                            Kai-Uwe Bux <jkherci...@gmx .netwrote
                            innews:ff7r0f$r g6$1@murdoch.ac c.Virginia.EDU:
                            It's neither. What he is doing is initialzing a reference member from
                            a temporary object. The lifetime of that object lasts exactly to the
                            end of the constructor call. Afterwards, i.e., for the entire lifetime
                            of the fully constructed object, the reference is dangling. From the
                            standard:
                            [...] A temporary bound to a reference member in a constructor?s
                            ctor-initializer (12.6.2) persists until the constructor exits. ...
                            [12.2/5]
                            This provision makes you wonder. What is the point of restricting the
                            life-time of the temporary to the duration of the constructor if the
                            object thus initialized is bound to have a dangling reference ever
                            after?
                            I don't have the Standard handy... but does it change the details since
                            it's a const-reference?
                            Yes. If the reference isn't to const, it can't be initialized
                            with a temporary at all, so the lifetime of such a temporary
                            doesn't have any meaning.

                            --
                            James Kanze (GABI Software) email:james.kan ze@gmail.com
                            Conseils en informatique orientée objet/
                            Beratung in objektorientier ter Datenverarbeitu ng
                            9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                            Comment

                            • James Kanze

                              #15
                              Re: Pimpl idiom without dynamic memory allocation

                              On Oct 18, 5:08 pm, Joe Greer <jgr...@doublet ake.comwrote:
                              Kai-Uwe Bux <jkherci...@gmx .netwrote
                              innews:ff7r0f$r g6$1@murdoch.ac c.Virginia.EDU:
                              It's neither. What he is doing is initialzing a reference member from
                              a temporary object. The lifetime of that object lasts exactly to the
                              end of the constructor call. Afterwards, i.e., for the entire lifetime
                              of the fully constructed object, the reference is dangling. From the
                              standard:
                              [...] A temporary bound to a reference member in a constructor?s
                              ctor-initializer (12.6.2) persists until the constructor exits. ...
                              [12.2/5]
                              This provision makes you wonder. What is the point of restricting the
                              life-time of the temporary to the duration of the constructor if the
                              object thus initialized is bound to have a dangling reference ever
                              after?
                              It is interesting. I would have thought that it would last until the scope
                              in which the constructor was invoked exited and the stack space is
                              reclaimed. There is probably some case for creating the temporaries within
                              the scope of the constructor or something that I don't see at the moment.
                              In any case, I can't see this for the pimpl idiom.
                              The temporary is created in the constructor, not in the code
                              which calls the constructor. The code which calls the
                              constructor doesn't even know that the temporary might exist.

                              --
                              James Kanze (GABI Software) email:james.kan ze@gmail.com
                              Conseils en informatique orientée objet/
                              Beratung in objektorientier ter Datenverarbeitu ng
                              9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                              Comment

                              Working...