0

I'm wrapping an existing library without introducing too much overhead such that the wrapper library could run as fast as the existing one. I'm doing this in order to make the interface(syntax) compatible with my old codes.

Say, the existing class is called BASE, which is templated class. I'm wrapping it as follows.

        template<class T>    
        class Wrapper{
        public:
           Wrapper() : base(){};
           /* ... */

          Wrapper<T> dosomething ( const Wrapper<T>& a )
          {
          Wrapper<T> output;
          output.base = CallExistingClass(a.base);
          return output;
          }

        private:
           BASE<T>   base;
            }; 

For any member function without return type, use this or *this produce very efficient code. However, when a return type of

  Wrapper<T>

is required, calling wrapped library is always 5-10 times slower. Since it is simply a wrapper class, all the manipulation I need is extracting member variable "base"(such as "a.base"), do something with a.base using the functions from existing class, then transfer the results to "output.base" and return "output".

I have to make sure the wrapper class match the old syntax of my old code, return by pointer is not an option here. The work-around I can think of is to return-by-reference with static variable.For example, declare this way and return-by-reference

      Wrapper<T>& dosomething ( const Wrapper<T>& a)
      {
      static Wrapper<T> output;
      output.base = CallExistingClass(a.base);
      return output;
      }

I wonder if there is faster way to do this without incurring overhead/temporary? Look forwarding to any useful comments.

BeSha
  • 47
  • 4
  • Just a small improvement. static in function makes it class wise static. Adding the `output` as member would make it a bit more thread-safe. – seleciii44 Dec 26 '17 at 07:03
  • that is a very good point, thank you sir. I really look forward to getting rid of temporary variable "output" by directly stealing the memory of the results. But I don't know how at this moment =D – BeSha Dec 26 '17 at 07:09
  • You could add move constructors to Base and Wrapper classes. That would be helpful too. See this answer: https://stackoverflow.com/a/1116763/1632887 – seleciii44 Dec 26 '17 at 07:27
  • Adding the "output" as member would make it a bit more thread-safe, it is possible to show some code to achieve this? AFAIK, only pointer to "output" can be added as a member, otherwise, compiler error "incomplete type" . Regarding move constructors, I already incorporate these, but only match the efficiency of direct calling BASE class when the computation size is large. Also, could you show some code for move constructors to verify I did it right. – BeSha Dec 26 '17 at 07:35
  • Using `static` and returning-by-reference cause different calls of `dosomething` returning the same object. Is that your intent? In addition, why not declare `dosomething` as `return-type dosomething () const` and use the member `base` instead of `a`? If you prefer to declare `a` as a parameter, I think it's better to declare `dosomething` as a static member function because it does not use private data members. – xskxzr Dec 26 '17 at 07:49
  • @xskxzr I'm not able to fully follow your reply, but it seems you offered a few different options. Do you care to elaborate further? Any code snippets is appreciated. The sole reason to use static and returning-by-reference is that returning-by-reference seems faster than returning-by-value, therefore I have to qualify the returned variable as static, otherwise it refers to garbage when the function returned. I will try to test your proposals. thank you. – BeSha Dec 26 '17 at 07:58
  • Example for move semantics: https://msdn.microsoft.com/en-us/library/dd293665.aspx, more explanation: https://learn.microsoft.com/en-us/cpp/cpp/rvalue-reference-declarator-amp-amp. Also read carefully the summary part of second link. Move semantics would help you in the case of dynamic memory allocations. – seleciii44 Dec 26 '17 at 07:59
  • For example, let's say `auto &x = w.dosomething(a); auto &y = w.dosomething(b)`, then `x` and `y` will refer to the same object, and you should take this case into consideration. In addition, don't worry about the performance of returning-by-value because of [copy elision](http://en.cppreference.com/w/cpp/language/copy_elision). – xskxzr Dec 26 '17 at 08:22
  • The declaration suggestions I mentioned just make your code more clear, and has nothing to do with the performance. I think my words are clear and don't know where you don't understand, and it is hard to post codes in comment, so I don't know how to elaborate further. – xskxzr Dec 26 '17 at 08:28
  • Well, I understand your words clearly. I just don't follow part of your answer as i'm still in the process of learning C++. For instance, you offered two options in the example above, <1>use the member base instead of a, <2> declare dosomething as a static member function because it does not use private data members. Is it possible to provide some snippets for these two cases to help the understanding. You can post in the answer section. Cheers. – BeSha Dec 26 '17 at 08:47

1 Answers1

2

One trivial optimization is to use initialization instead of assignment, e.g.

template <class... U>
Wrapper(U&&... args) : base(std::forward<U>(args)...) {}  // forward constructor  

Wrapper<T> dosomething ( const Wrapper<T>& a )
{
    Wrapper<T> output{CallExistingClass(a.base)};
    return output;
}

Because of copy elision, this can avoid not only the unnecessary value-initialization, but also any copy operation.


In addition, to call dosomething on an object a, for the current declaration you should create an extra object (say w), and call it in the form w.dosomething(a). To improve this, there are two ways.

(1) Note the effect of dosomething does not depend on data members, so you can declare it as static, e.g.

static Wrapper<T> dosomething ( const Wrapper<T>& a )
{
    //...
}

then you can use it in the form Wrapper<T>::dosomething(a) without creating an extra object.

(2) Also, you can use the data member instead of the parameter, e.g.

Wrapper<T> dosomething const ()
{
    Wrapper<T> output{CallExistingClass(base)};
    return output;
}

and call it in the form a.dosomeghing().

xskxzr
  • 12,442
  • 12
  • 37
  • 77