0

I have a struct called Edge with memebers pair<double,double> s and pair<double,double> e.

In the main function,I have an vector of edges vector<Edge> edges. I populate that with Edge objects.

Now I want to find the index of a particular Edge x. So I wrote a function :

int indexOf(vector<Edge>& arr,Edge& k);

Body of function is irrelevant here. Now my question is, How can make the function work like :

edges.indexOf(x)

without having to pass the vector of edges as a parameter?

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
VerilogCoder
  • 106
  • 7
  • 1
    You can't. That would require adding a member function to `std::vector` which is not possible (without modifying the standard library). – super Jun 03 '21 at 05:06
  • okay thank you!In c# we could extend and dd it.I thought something similar in cpp exists too. – VerilogCoder Jun 03 '21 at 05:08
  • 3
    You can do this through inheritance, but I would not recommend it. Having external functions is more flexible, easier on maintenance. – Galik Jun 03 '21 at 05:31

4 Answers4

3

Maybe inherit std::vector?

#include <iostream>
#include <vector>

using namespace std;

struct Edge
{
    Edge(double _s, double _e) : s(_s), e(_e) {}
    double s;
    double e;
};

bool operator==(const Edge &lhs, const Edge &rhs) {
    return lhs.s == rhs.s && lhs.e == rhs.e;
}

class MyVector : public vector<Edge>
{
public:
    int index_of(const Edge &e)
    {
        for (int i = 0; i < this->size(); ++i) {
            if (this->at(i) == e)
                return i;
        }
        return -1;
    }
};

int main()
{
    MyVector v;
    for (int i = 0; i < 10; ++i)
        v.emplace_back(i, i);
    cout << v.index_of(v.at(5)) << endl;
    return 0;
}
CW-B-W
  • 46
  • 3
3

Now my question is, How can make the function work like edges.indexOf(x)

Since inheriting a std::vector is not a good solution (it adds caveats to the inherited code) the good solution is to encapsulate your vector:

class EdgeSequence // or another name
{
public:
    size_t indexOf(const Edge& x) const
    {
        return std::distance(
            begin(),
            std::find(begin(), end(), x));
    }

    auto begin() const { return data.begin(); }
    auto end() const { return data.end(); }

    // TODO: add other functions you used on your initial vector
private:
    std::vector<Edge> data;
};

without having to pass the vector of edges as a parameter?

EdgeSequence edges; // TODO: fill with data
auto index = edges.indexOf(your_edge);
utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • 1
    You solution is nice and simple to illustrate the concept (which is similar to what I was promoting and perhaps less daunting) and therefore I upvoted. I would just wrap the reference to the container though, and make indexOf const as it doesn't modify state (ditto begin and end). Keeping const reference to data might be debatable, but a little documentation to indicate the lifetime of has to extend that of the utility should do? – Werner Erasmus Jun 03 '21 at 08:42
0

I've also given this a stab. I must say I quite like what one is able to do in c# in that case.

I've tried to stay clear of inheritance and the answer here elaborates why. In general I rarely inherit implementation (or extend as c# calls it).

One can create a class like IndexCheckable below that wraps the container. Presently it is only implemented for vector, but one can easily implement it for other containers too (and use other algorithms to determine the index).

IndexCheckable has a clear responsibility and gives checkability to it's container. One can also hide it behind an interface that hides the type of the container (not shown in example)

#include <iostream>
#include <vector>
#include <algorithm>

template <class T, class Allocator = std::allocator<T>>
struct IndexCheckable {
    const std::vector<T,Allocator>& indexable_;
    IndexCheckable(const std::vector<T,Allocator>& indexable)
    : indexable_(indexable)  {
    }
    
    int indexOf(const T& item) const {
        auto i = std::find(indexable_.begin(), indexable_.end(), item);
        return i == indexable_.end() ? -1 : i - indexable_.begin();
    }
};
template <class T, class Allocator = 
std::allocator<T>> IndexCheckable<T,Allocator> indexCheckable(const std::vector<T,Allocator>& indexable) {
    return IndexCheckable<T,Allocator>{indexable};
}

void foo(const IndexCheckable<int>& indexCheckable) {
  //Voilla. Check works!!!
  indexCheckable.indexOf(5);        
}



int main() {
    std::vector<int> ivect{0, 1, 2, 5, 6};
    foo(ivect);
    
    // or...
    auto r1 = indexCheckable(ivect).indexOf(5);
    std::cout << "r1: " << r1 << std::endl; //Expects 3
    
    auto r2 = indexCheckable(ivect).indexOf(10);
    std::cout << "r2: " << r2 << std::endl; //Expects -1
    
    return 0;
}

I've also decided to elaborate on the idea of hiding IndexCheckable behind and interface and implementing this for different containers. From a usage perspective, a client of the code just has to specify that something must be IndexCheckable (and oblivious of the container type):

#include <iostream>
#include <vector>
#include <list>
#include <array>
#include <algorithm>
#include <iterator>

template <class T>
struct IndexCheckable {
    virtual int indexOf(const T& item) const = 0;
    protected: ~IndexCheckable(){}
};

template <
    template <class, class> class C, //Container
    class T, //Item
    class A = std::allocator<T>
>
struct IndexCheckableImpl : public IndexCheckable<T> {
    const C<T, A>& indexable_;
    IndexCheckableImpl(const C<T, A>& indexable)
    : indexable_(indexable)  {
    }
    
    int indexOf(const T& item) const override {
        auto i = std::find(indexable_.begin(), indexable_.end(), item);
        return i == indexable_.end() ? -1 : std::distance(indexable_.begin(), i);
    }
};

template <template<class,class> class C, class T, class A = std::allocator<T>> 
IndexCheckableImpl<C,T,A> indexCheckable(const C<T,A>& indexable) {
    return IndexCheckableImpl<C,T,A>{indexable};
}

void testItem(const IndexCheckable<int>& indexCheckable, const char* name) {
    auto r1 = indexCheckable.indexOf(5);
    std::cout << "Test " << name << ": r1: " << r1 << std::endl; //Expects 3
    
    auto r2 = indexCheckable.indexOf(10);
    std::cout << "Test " << name << ": r2: " << r2 << std::endl; //Expects -1
}


template <class Container>
void test(const Container& container, const char* name) {
    auto checkable = indexCheckable(container);
    testItem(checkable, name);
}

int main() {
    test(std::vector<int>{0, 1, 2, 5, 6}, "vector");
    test(std::list<int>{0, 1, 2, 5, 6}, "list");
    return 0;
}
Werner Erasmus
  • 3,988
  • 17
  • 31
-1

buddy u can do this my providing the default value at function definition example :- int indexOf(Edge& k,vector& arr = yourVector);

but just make sure you declare the yourVector as golbal variable and put value in yourVector before using the function. If u want sample code comment me.