mardi 29 janvier 2019

Are there algorithms are or patterns that can be applied to polymorphic class - methods?

Historically, I've been using trait classes to hold information and apply that into a "generic" function that runs the same "algorithm." Only differed by the trait class. For example: https://onlinegdb.com/ryUo7WRmN

enum selector { SELECTOR1, SELECTOR2, SELECTOR3, };

// declaration
template < selector T> struct example_trait;

template<> struct example_trait<SELECTOR1> {  
static constexpr size_t member_var = 3;  
static size_t do_something() { return 0; }
};

template<> struct example_trait<SELECTOR2> {  
static constexpr size_t member_var = 5; 
static size_t do_something() { return 0; }  
};


// pretend this is doing something useful but common
template < selector T, typename TT = example_trait<T> > 
void function() { 
std::cout << TT::member_var << std::endl; 
std::cout << TT::do_something() << std::endl;
}

int main()
{
    function<SELECTOR1>();
    function<SELECTOR2>();
    return 0;
}

I'm not sure how to create "generic" algorithms this when dealing with polymorphic classes.

For example: https://onlinegdb.com/S1hFLGC7V

Below I have created an inherited class hierarchy. In this example I have a base catch-all example that defaults all the parameters to something (0 in this case). And then each derived class sets overrides specific methods.

#include <iostream>
#include <memory>
#include <type_traits>
#include <assert.h>    

using namespace std;

struct Base {
 virtual int get_thing_one() {
     return 0;
    }

 virtual int get_thing_two() {
     return 0;
    }

 virtual int get_thing_three() {
     return 0;
    }

 virtual int get_thing_four() {
     return 0;
    }    
};

struct A : public Base {
    virtual int get_thing_one() override {
     return 1;
    }

  virtual int get_thing_three() override {
     return 3;
 }  
};

struct B : public Base {
    virtual int get_thing_one() override {
     return 2;
    }    

    virtual int get_thing_four() override{
     return 4;
 }    
};

Here I created a simple factory, not elegant but for illustrative purposes

// example simple factory
std::shared_ptr<Base> get_class(const int input) {
    switch(input)
    {
        case 0:
            return std::shared_ptr<Base>(std::make_shared<A>());
        break;

        case 1:
            return std::shared_ptr<Base>(std::make_shared<B>());
        break;

        default:
            assert(false);
        break;
    }
}

So this is the class of interest. It is a class does "something" with the data from the classes above. The methods below are a simple addition example but imagine a more complicated algorithm that is very similar for every method.

// class that uses the shared_ptr
class setter {
    private:

    std::shared_ptr<Base> l_ptr;

    public:

    setter(const std::shared_ptr<Base>& input):l_ptr(input)
    {}

    int get_thing_a()
    {
        return l_ptr->get_thing_one() +  l_ptr->get_thing_two();
    }

    int get_thing_b()
    {
        return l_ptr->get_thing_three() +  l_ptr->get_thing_four();
    }
};

int main()
{
    constexpr int select = 0;
    std::shared_ptr<Base> example = get_class(select);
    setter l_setter(example);

    std::cout << l_setter.get_thing_a() << std::endl;
    std::cout << l_setter.get_thing_b() << std::endl;

    return 0;
}

How can I make the "boilerplate" inside the setter class more generic? I can't use traits as I did in the example above because I can't tie static functions with an object. So is there a way to make the boilerplate example more common?

Aucun commentaire:

Enregistrer un commentaire