简体   繁体   中英

Templates With Psuedo-Inheritance Design C++

This question might fall into "wanting the best of all worlds" but it is a real design problem that needs at least a better solution.

Structure needed: 在此输入图像描述

In order of importance, here's the requirements that have me stuck

  • We need templates, whether on the class or function level. We are highly dependent on template objects in arguments of functions at this point. So if anything leaves the model below, its virtual functions (to my knowledge).
  • We want to decouple the call from selection. By that we want the user to declare a Math Object and have the background figure it out, preferably at runtime.
  • We want there to be a default, like shown in the above diagram.

In my company's program, we have a crucial algorithm generator that is dependent on both compile-time and runtime polymorphism, namely template classes and virtual inheritance. We have it working, but it is fragile, hard to read and develop and has certain features that won't work on higher optimization levels (meaning we are relying on undefined behavior somewhere). A brief outline of the code is as follows.

// Math.hpp
#include <dataTypes.hpp>

// Base class. Actually handles CPU Version of execution
template <typename T>
class Math {
    // ...

    // Example function. Parameters vary in type and number
    // Variable names commented out to avoid compile warnings
    virtual void exFunc ( DataType<T> /*d*/, float /*f*/ )
    {
        ERROR_NEED_CODE; // Macro defined to throw error with message
    }

    // 50+ other functions...
};

//============================================================
// exampleFuncs.cpp
#include<Math.hpp>

template <> void Math<float>::exFunc ( DataType<float> d, float f)
{
    // Code Here.
}

Already, we can see some problems, and we haven't gotten to the main issue. Due to the sheer number of functions in this class, we don't want to define all in the header file. Template functionality is lost as a result. Second, with the virtual functions with the template class, we need to define each function in the class anyways, but we just shoot an error and return garbage (if return needed).

//============================================================
// GpuMath.hpp
#include <Math.hpp>

// Derived class. Using CUDA to resolve same math issues
GpuMath_F : Math<float> { ... };

The functionality here is relatively simple, but I noticed that again, we give up template features. I'm not sure it needs to be that way, but the previous developers felt constrained to declare a new class for each needed type (3 currently. Times that by 50 or so functions, and we have severe level of overhead).

Finally, When functionality is needed. We use a Factory to create the right template type object and stores it in a Math pointer.

// Some other class, normally template
template <typename T>
class OtherObject {
    Math<T>* math_;

    OtherObject() {
         math_ = Factory::get().template createMath<T> ();
         // ...
    }
    // ...
};

The factory is omitted here. It gets messy and doesn't help us much. The point is that we store all versions of Math Objects in the base class.

Can you point me in the right direction for other techniques that are alternative to inheritance? Am I looking for a variation of Policy Design? Is There a template trick?

Thanks for reading and thanks in advance for your input.

As has been discussed many times before , templates with virtual features don't jive well together. It is best to choose one or the other.

Approach 1 : Helper Class

The first and best option we have so far does just that, opting out of the virtual features for a wrapper class.

class MathHelper {
    Math cpuMath;
    GpuMath gpuMath;
    bool cuda_; //True if gpuMath is wanted

    template <typename T>
    void exFunc ( DataType<T> d, float f )
    {
        if (cuda_)
            gpuMath.exFunc( d, f );
        else
            cpuMath.exFunc( d, f );
    }
    // 50+ functions...
};

First, you might have noticed that the functions are templated rather than the class. It structurally is more convenient.

Pros

  • Gains full access to templates in both CPU and GPU classes.
  • Improved customization for each and every function. Choice of what is default.
  • Non-invasive changes to previous structure. For example, if this MathHelper was just called Math and we had CpuMath and GpuMath as the implementation, the instantiation and use can almost be the same as above, and stay exactly the same if we let Factory handle the MathHelper.

Cons

  • Explicit if/else and declaration of every function.
  • Mandatory definition of every function in MathHelper AND at least one of the other Math objects.
  • As a result, repeated code everywhere.

Approach 2: Macro

This one attempts to reduce the repeated code above. Somewhere, we have a Math function.

class Math {
    CpuMath cpuMath;
    GpuMath gpuMath;
    // Some sort of constructor
    static Math& math() { /*static getter*/ }
};

This math helper uses a static getter function similar to Exam 1 shown here . We have base class CpuMath that contains no virtual functions and derived class GpuMath . Again, templating is on function level.

Then from there, any time we want a math function we use this macro:

#define MATH (func, ret...) \
do { \
    if (math.cuda_) \
        ret __VA_OPT__(=) math().cuda.func; \
    else \
        ret __VA_OPT__(=) math().cpu.func; \
} while (0)

Pros

  • Remove repeat code of previous wrapper.
  • Again, full power of templates unlocked

Cons

  • Not as customizable as above wrapper
  • Initially much more invasive. Every time a Math function is accessed, it has to change from val = math_.myFunc(...) , to MATH (myFunc(...), val) . Because editors don't do good error checking on macros, this has potentially to cause many errors in the editing process.
  • Base class must have every function derived class have, since it is default.

Again, if any other creative ways around to implement this design would be appreciated. I found this to be a fun exercise either way, and would love to continue learning from it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM