0

Here is the code:

#include <iostream>


class Interface_A
{
  public:virtual bool dothething () = 0;
};


class Inherit_B:public Interface_A
{
  bool dothething ()
  {
    std::cout << "Doing the thing in B\n";
    return true;
  }
};

class Inherit_C:public Interface_A
{
  bool dothething ()
  {
    std::cout << "Doing the thing in C\n";
    return true;
  }
};

/**This works */
Interface_A& makeBC ()
{
 #ifdef make_B
  return *(new Inherit_B ());
 #elif make_C
  return *(new Inherit_C());
 #endif
}

/**This doesn't work 
Interface_A makeC ()
{
 #ifdef make_B
  return ((Interface_A) (new Inherit_B ()));
 #elif make_C
  return ((Interface_A) (new Inherit_C ()));
 #endif
}
*/

int main ()
{
  Interface_A& obj = makeBC ();
  obj.dothething();
  // ultimate goal is to make a vector of type <Interface_A&>
  return 0;
}

I want to ultimately create a vector of type <Interface_A&>, but I cannot seem to find a way to do this. Creating a vector of type <Interface_A> will also work, but as far as I understand it is not allowed by c++ to create pointer references to an abstract type.
I cannot use return type Inherit_B because Interface_A is being inherited by multiple classes and the active class is determined during compile-time. I cannot use smart pointers because performance is an extremely critical aspect of the code.
How do I make a generic solution to this?

doa4321
  • 176
  • 2
  • 9
  • And what is wrong with `std::vector`, exactly? Or even better a `std::vector>`? – Botje Feb 16 '23 at 09:12
  • 2
    `Interface&`, as any other reference is NOT an object in C++ so you cannot create an object container of them. `reference_wrapper` is out there. – Swift - Friday Pie Feb 16 '23 at 09:39
  • *"I cannot use smart pointers because performance is an extremely critical aspect of the code."* -- Do you have experimental evidence to back this up? Theoretically, a reference has the same performance characteristics as a raw pointer. A raw pointer that is not leaked has the same performance characteristics as a `unique_ptr` (in typical usage). So, where is the gain from using references? – JaMiT Feb 16 '23 at 10:02
  • @JaMiT I have excessive evidence to back up my statement. The reason why I am trying to not use smart pointers is because [Intel vTunes profiler](https://www.intel.com/content/www/us/en/developer/tools/oneapi/vtune-profiler-download.html?operatingsystem=window&distributions=webdownload&options=offline) showed that using smart pointers and maps are two most runtime consuming operations that are being done in the code. I intend to use raw pointers, but the problem is I am working on a different team's code base and I do not want to change their function signatures returning references. – doa4321 Feb 16 '23 at 10:08
  • if you build a house then a profiler will show you that lots of time is spend using a hammer. From that alone you cannot not conclude that a hammer is the wrong tool, but rather that there are a lot of nails to be hammered. – 463035818_is_not_an_ai Feb 16 '23 at 11:14
  • 1
    Why do you think smart pointers are more expensive than references? The assembly generated by using a `std::unique_ptr` and a `T&` is going to be indistinguishable, except the `unique_ptr` will remember to delete the object and the `T&` will leak. – Yakk - Adam Nevraumont Feb 16 '23 at 16:29

1 Answers1

4

std::reference_wrapper lets you store references in container. However, you do not need references. References refer to objects. What you need is to store the object somewhere and keep a pointer to it in the vector. Thats a std::vector<std::unique_ptr<Interface>>.

I cannot use smart pointers because performance is an extremely critical aspect of the code.

Thats a moot point. You already bought in a level of indirection when you decided to use inheritance and runtime polymorphism. The smart pointers bring no additional overhead, but they are the right tool for the situation you are already in.

If you want to avoid that level of indirection then you should reconsider the use of runtime polymorphism.

... the active class is determined during compile-time

And there we go... you do not need runtime polymorphism. Your example is too vague and abstract to suggest a better design (templates?). But when you do not need runtime polymorphsim, then of course you do not want to pay for it.


Creating a vector of type <Interface_A> will also work, but as far as I understand it is not allowed by c++ to create pointer references to an abstract type.

You could make the base class non-abstract, but then a vector<Interface_A> will not be the right thing to use, due to object slicing. See What is object slicing?

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • `What you need is to store the object somewhere and keep a pointer to it...` That's appropriate for time-critical. `Thats a std::vector>` No, in most cases it's not. Especially in OPs. He'd prefer to use some free-store replacement, a memory pool of some kind. – Swift - Friday Pie Feb 16 '23 at 09:44
  • 1
    @Swift-FridayPie can they not use whatever kind of allocation and the unique_ptr? Not sure, but actually I think the important point is that they need no pointers or inheritance at all. Or maybe they do. From the question and posted code it is not clear why they use an abstract `Interface_A` in the first place. It seems like it can all be templates. – 463035818_is_not_an_ai Feb 16 '23 at 09:47
  • True, but I can suspect the reasons, because I work with similar kind of projects. A common case when such thing is a time-critical thing is a communication protocol's API for something with time-limited responses. The interface is likely then either API to operate the data source or to describe common operations. It CAN be done with static polymorphism in some cases unless project's architecture supposes to operate with different data types in bulk, i.e. to work with message packets of different kind in same place and data changes dynamically. – Swift - Friday Pie Feb 16 '23 at 09:52
  • @Swift - you guessed it correctly. This is indeed a communication interface layer that I am trying to optimize. – doa4321 Feb 16 '23 at 10:02
  • @463035818_is_not_a_number if I were to go by your example, can you edit your answer or tell me how to use the unique_ptr over here? I am pretty certain I cannot return a `unique_ptr` from the `makeBC()` method. I am limited to use C++11 standards, so `make_unique` is also not an option. It is extremely frustrating to be limited by so many conditions and trying to find a generic solution while being compliant with the DRY principle. – doa4321 Feb 16 '23 at 10:05
  • @doa4321 There is one scenario that doesn't require pointers at all. Consider there is a store that describes large enough buffer for a stream or a datagram. Then there is some kind of range-view abstraction for packets that describes its size, header, etc but it doesn't store it - it just stores "ether" indexes in the store. And then there is a layer that implements individual packets, with a virtual interface, but they operate via that range. This got a drawback - I have to know the size of packet when allocating it and I shouldn't change the size afterward. Google protobuf helped with that. – Swift - Friday Pie Feb 16 '23 at 10:12
  • 1
    @doa4321 `make_unique` is only for convenience, and it is what makes purist have their "never raw `new`" chanting ;). You can see a possible implementation here https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique, not too compilcated, if you like you can have it in c++11 too. – 463035818_is_not_an_ai Feb 16 '23 at 10:26
  • @doa4321 maybe later I can find time to add more to the answer... – 463035818_is_not_an_ai Feb 16 '23 at 10:26
  • @Swift-FridayPie thank you for the `std::reference_wrapper` suggestion. That resolved my problem :) – doa4321 Feb 20 '23 at 06:46