AlmostSurely
AlmostSurely

Reputation: 562

Passing parameters separated by commas to << operator

I'm trying to accomplish something similar to Eigen advanced initialization for my container class.

i.e. in main, I want to fill an object of type DynamicArray as follows:

main.cpp

// Create 3 x 3 Dynamic array and fill with 0's
DynamicArray da(3, 3, 0);

da << 1, 2, 3,
      4, 5, 6,
      7, 8, 9;

The method I've used to try to accomplish this is:

DynamicArray.hpp

template <typename T>
class DynamicArray {

        // ...

public:

        // ...

    template <typename... Args,
              typename = typename std::enable_if<ArgPackSameTruth<
                                             std::is_convertible<Args, T>...>::value,
                                             T>::type
    > void operator<<(Args... x) {

        typename std::vector<T> arg_vect{std::forward<Args>(x)...};

        std::cout << arg_vect.size() << "\n";

        // Load contents of arg_vect into *this...

        return;
    }
}

When I compile main.cpp, I get a size of 1 for arg_vect, so only the first parameter is being taken in. How can I make sure all parameters are passed?

Thanks!

Upvotes: 1

Views: 92

Answers (2)

en4bz
en4bz

Reputation: 1132

operator << only takes 1 argument. That's why the size is 1. The numbers after 1 are all unused. If you turn on -Wall you should get a warning about this.

I can think of 2 solutions.

One would be to simply enclose the numbers in braces {...} and let operator << accept a std::initializer_list<double>

void operator<< (std::initializer_list<double>);

Second solution would be to have Dynamic array overload operator,and operator<<

Both , and << would return a DynamicArray so that they could be chained.

DynamicArray& operator<<(double x){
    //Add x to the Dynarray
    return *this;
}

operator, Would do the same thing as operator<<

DynamicArray& operator, (double x){
    //Add x to the Dynarray
    return *this;
}

Here is a baisic example for std::vector

#include <vector>
#include <iostream>

template <typename T>
std::vector<T>&
operator<<(std::vector<T>& x, T y)
{
    x.push_back(y);
    return x;
}

template <typename T>
std::vector<T>&
operator,(std::vector<T>& x, T y)
{
    x.push_back(y);
    return x;
}

int main(){
    std::vector<int> A;
    A << 1,2,3,4,5,6,7,8,9,10;
    for(int x : A){
        std::cout << x << ",";
    }
    std::cout << std::endl;
}

Upvotes: 2

Daniel Frey
Daniel Frey

Reputation: 56863

The order of operations is different from what you seem to think. If you have:

da << 1, 2, 3;

it is equivalent to (given free operators):

auto tmp1 = operator<<(da,1);
auto tmp2 = operator,(tmp1,2);
auto tmp3 = operator,(tmp2,3);

hence you'd need operators roughly like this:

#include <iostream>

struct DynamicArray
{
};

DynamicArray& operator<<( DynamicArray& da, int i )
{
    std::cout << "<<" << i << std::endl;
    return da;
}

DynamicArray& operator,( DynamicArray& da, int i )
{
    std::cout << "," << i << std::endl;
    return da;
}

int main()
{
    DynamicArray da;
    da << 1, 2, 3,
          4, 5, 6,
          7, 8, 9;
}

Live example

Of course you need to store the values somewhere, etc. Think about a design which allows this and consider adding an intermediate class to hold the values which is returned by operator<< and which works as both the first input parameter and the return type for operator,. The destructor of that class can be used to apply the collected intermediate data if needed.

Upvotes: 5

Related Questions