1

I learned the concept of polymorphism and I am trying to use the concept in order to create a map called "pipeline" that takes a vector of inputs (the idea is that these could be different data structure) and applies to the input a function (the function could also be selected differently in different use cases) and which produce a vector of results (which also change from case to case). My idea was to try the following

#include <iostream>
#include <vector>

using namespace std;


struct input
{
};

struct intInput : public input
{
    intInput(int x) :x(x) {}
    int x;
};

struct result
{
};

struct intResult : public result
{
    intResult(int y) :y(y) {}
    int y;
};

vector<result> pipeline(vector<input> x, result(*g)(input))
{
    std::vector<result> res;
    for (size_t i = 0; i < x.size(); i++)
    {
        res.push_back((*g)(x[i]));
    }
    return res;
}

intResult doubling(intInput i)
{
    intResult res(2 * i.x);
    return res;
}

void printing(input i)
{
    cout << "hi" << endl;
}

int main()
{
    vector<intInput> v{ 1,2,3,4 };
    pipeline(v, doubling);
}

but I get the compilation error

'std::vector<result,std::allocator<result>> pipeline(std::vector<input,std::allocator<input>>,result (__cdecl *)(input))': cannot convert argument 1 from 'std::vector<intInput,std::allocator<intInput>>' to 'std::vector<input,std::allocator<input>>'

Is there a way to fix this?

roi_saumon
  • 489
  • 4
  • 13
  • They are not compatible, and they can't be - vector is an array of input objects (1 byte each, because that's the minimum size) while vector is an array of intInput objects (4 bytes each) and if you try to treat the vector as a vector the address calculations won't match. – user253751 Jan 18 '23 at 10:46
  • A side note: better to avoid `using namespace std` - see here https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice. – wohlstad Jan 18 '23 at 10:48

1 Answers1

2

vector<input> and vector<intInput> aren't polymorphic and will never be - vector<input> knows that each entry is 1 byte long (the minimum size) and vector<intInput> knows that each entry is 4 bytes long.

However, you could achieve what you want by making pipeline a template, for example:

template<typename MyResult, typename MyInput>
vector<MyResult> pipeline(vector<MyInput> x, MyResult(*g)(MyInput))
{
    std::vector<MyResult> res;
    for (size_t i = 0; i < x.size(); i++)
    {
        res.push_back((*g)(x[i]));
    }
    return res;
}

No inheritance is required. You can delete the classes input and result. The compiler will be able to generate variants of this function as needed, like these ones:

vector<intResult> pipeline<intInput, intResult>(vector<intInput> x, intResult(*g)(intInput))
vector<int> pipeline<int, int>(vector<int> x, int(*g)(int))
vector<string> pipeline<float, string>(vector<float> x, string(*g)(float))

You can call the function as pipeline<intInput, intResult>(v, doubling);, but you can also leave out the <> part when the compiler is able to work it out by itself, so you can call pipeline(v, doubling);

Note that if you declare this function* in a header file, the definition should also be in the same header file and not in a .cpp file. That's because every time the compiler sees you call the function, it has to create the correct version of the function for that call, therefore it has to know what code is in the function.

* technically it is a function template, not a function


Inheritance polymorphism in C++ only works with pointers or references. That's because all pointers are the same size. Code that handles a input* doesn't have to care there are actually some extra variables after the end of the input that it doesn't know about. (Unless it uses pointer arithmetic! Pointer arithmetic isn't polymorphic)

You could have a vector of pointers (vector<input*>) but this is usually a little bit unwieldy. Or you could make some thing that returns pointers: (done here with pointers throughout, but references also work)

struct inputGetter {
    virtual input* getInputPointer(int index) = 0;
    virtual size_t size() = 0;
};

struct intInputVectorGetter : public inputGetter {
    vector<intInput>* theVector;
    intInputVectorGetter(vector<intInput>* theVector) {
        this->theVector = theVector;
    }
    input* getInputPointer(int index) override {
        return &theVector[index];
    }
    size_t size() override {
        return theVector.size();
    }
};

// make a resultSetter, too

void pipeline(resultSetter* y, inputGetter* x, void(*g)(output*, input*)) {
    y->clear();
    y->setSize(x->size());
    for (size_t i = 0; i < x->size(); i++)
    {
        (*g)(y->getResultPointer(i), x->getInputPointer(i));
    }
}

As you can see it is substantially uglier in order to prevent the pipeline function from handling any of the actual result objects - only the pointers. For this pipeline function, the template solution really is better.

user253751
  • 57,427
  • 7
  • 48
  • 90
  • OP's point is to learn about polymorphism. There is no polymorphism in this suggested solution. – j6t Jan 18 '23 at 10:59
  • @j6t, is it possible with polymorphism? – roi_saumon Jan 18 '23 at 11:05
  • I think it will be possible to have *some* solution that uses polymorphism. However, I guess it will be a solution in search of a problem (IOW: too general to be useful; a solution of the [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it) kind). I think you better study polymorphism with classes of the kind Animal, Bird, Mammal, Parrot, Duck, Dog, and Horse; but not with a computing pipeline that can do anything with everything. – j6t Jan 18 '23 at 11:16
  • @roi_saumon I showed how you might do it if you really want to use polymorphism but it's uglier. By the way some people would argue that templates should also be called polymorphism, since the word "polymorphism" should actually mean that you can type `pipeline(x, doubling)` and it doesn't matter whether x is ints or doubles - and templates do do that. – user253751 Jan 18 '23 at 12:10
  • 1
    @user253751, yes static polymorphism vs dynamic polymorphism. Thank you! – roi_saumon Jan 18 '23 at 12:11