PierreBdR
PierreBdR

Reputation: 43234

"Automatic" class proxy in C++

I need to allow the user to change members of two data structures of the same type at the same time. For example:

struct Foo { int a, b; }

Foo a1 = {1,2}, a2 = {3,4};
dual(a1,a2)->a = 5;
// Now a1 = {5,2} and a2 = {5,2}

I have a class that works and that change first a1 and then copy a1 into a2. This is fine as long as:

Is there a way to obtain this behavior:

dual(a1,a2)->a = 5;
// Now a1 = {5,2} and a2 = {5,4}

I am opened to alternative syntax, but they should stay simple, and I would like to avoid things like:

set_members(a1, a2, &Foo::a, 5);
members(a1, a2, &Foo::a) = 5;

or anything involving specifying explictely &Foo::

[Edit]

I should be more precise. The point is to work with a graph library. The library works on directed graph, but usage dictate that given two vertices, v1 and v2, if there is an edge v1->v2, then there will be an edge v2->v1. And these two edges have, very often (but not always) the same properties. So the current implementation now allows:

G.edge(v1,v2)->b = 5; // Only v1->v2 is modified
G.arc(v1,v2)->a = 10;
// Now G.edge(v2,v1) is set to G.edge(v1,v2) after the modification a = 10 (i.e. b = 5 too)

And I would like the notation to imply that only a is modified.

Upvotes: 2

Views: 741

Answers (5)

Winston Ewert
Winston Ewert

Reputation: 45039

By introducing an abuse of templates I can get most of the desired syntax. This compiles and works, but no guarantees are made about it. It requires adding some macros the struct to be used and requires use of set_* instead of direct assignment.

#include <iostream>

#define PROPERTY_MAP(ClassName) \
    struct hidden_Mapper {      \
        ClassName * m_d1;       \
        ClassName * m_d2;       \
        hidden_Mapper(Data * d1, Data * d2) : \
            m_d1(d1), m_d2(d2) {} 

#define DECLARE_PROPERTY(name)\
    template <typename ValueType> \
    void set_##name(const ValueType & value) \
    { m_d1->name = value; m_d2->name = value; } \

#define END_PROPERTY_MAP };


template<typename ClassType>
typename ClassType::hidden_Mapper dual(ClassType & d1, ClassType & d2)
{
    return typename ClassType::hidden_Mapper(&d1, &d2);
}

struct Data
{
    int a;
    float b;

    PROPERTY_MAP(Data)
        DECLARE_PROPERTY(a)
        DECLARE_PROPERTY(b);
    END_PROPERTY_MAP
};

int main()
{
    Data d1, d2;
    dual(d1, d2).set_a(5);
    dual(d1, d2).set_b(5.7);
    std::cout << d1.a << d2.a << d1.b << d2.b <<std::endl;
}

Upvotes: 0

Alsk
Alsk

Reputation: 1118

//to get the desired syntax

template<class T>
class SetPropertyProxy
{
public:
   SetPropertyProxy(T& _v1, T& _v2)
     : a(_v1, _v2) {}

   class A_Property_Proxy
   {
   public:
       A_Property_Proxy(T& _v1, T& _v2): v1(_v1), v2(_v2) {}
       A_Property_Proxy& operator = (T::A_Property_Type val)
       {
           v1.a = val;
           v2.a = val;
           return *this;
       }
   private:
       T& v1;
       T& v2;
   }
   //public member "a"
   A_Property_Proxy a;
};
//helper function
template<class T>
SetPropertyProxy<T> dual(T& a , T& b)
{ return SetPropertyProxy<T>(a,b); }
//usage
dual(a,b).a = 5; //calls A_Property_Proxy::operator =

It can be improved further making A_Property_Proxy class reusable by parameterizing by property type and taking references to properties instead of references to property containers (edges in this case)

   template<class U>
   class Property_Proxy
   {
   public:
       Property_Proxy(U& _v1prop, U& _v2prop): v1prop(_v1prop), v2prop(_v2prop) {}
       Property_Proxy& operator = (U val)
       {
           v1prop = val;
           v2prop = val;
           return *this;
       }
   private:
       U& v1prop;
       U& v2prop;
   }

Upvotes: 2

egrunin
egrunin

Reputation: 25053

Edit (putting this here because comments don't have formatting)

So are you saying that your current code has lots of this:

G.edge(v3,v4)->a = 2;
G.edge(v3,v4)->b = 2;
G.edge(v4,v5)->a = 6;
G.edge(v4,v5)->b = 6;

And a very little bit of this:

G.edge(v5,v6)->a = 4;
G.edge(v5,v6)->b = 7;

And your goals are [1] make it easier to spot those special cases [2] less verbose code?

----- original answer, may be irrelevant now -----

Here's a general idea, there are lots of possible improvements:

class MagicBag
{
private:
    // you could make the whole class a template 
    // instead of hard-coding Foo..
    vector<Foo *> m_vec;

public:
    // store references to the items
    void Add(Foo *f) { m_vec->push_back(f); }

    // you can do overloads instead of these setters...
    void set_a(int val) {
        for (vector<Foo>::iterator i = m_vec.start(); i != m_vec.end(); ++i)
            (*i)->a = val;
    }

    void set_b(int val) {
        for (vector<Foo>::iterator i = m_vec.start(); i != m_vec.end(); ++i)
            (*i)->b = val;
    }
}

Usage:

Foo a1 = {1,2}, a2 = {3,4};
MagicBag mb;

mb.Add(&a1);
mb.Add(&a2);
mb.set_a(5); // now a1.a = 5 and a2.a = 5
// etc.

This is easier semantically in languages which support Properties, such as C#. There the final syntax would be:

mb.a = 5;

Upvotes: 0

scjohnno
scjohnno

Reputation: 149

Relatively simple solution with Boost.Lambda:

#include <boost/lambda/lambda.hpp>

using namespace boost::lambda;

template<typename T, typename U, typename V>
void dual(const T& functor, U& a1, V& a2)
{
    functor(a1);
    functor(a2);
}

struct Foo
{
    int a;
};

struct Bar
{
    char a;
};

int main()
{
    Foo a1;
    Bar a2;

    dual(_1 = 5, a1.a, a2.a);
}

Extending dual() with variadic templates / Boost.Preprocessor shenanigans is left as an exercise to the reader.

Upvotes: 2

Anycorn
Anycorn

Reputation: 51465

struct proxy {
  struct column {
     column(T &a, T &b);
     column& operator=(T);
     T &a, &b;
  };
  proxy(U &A, U &B);
  column operator[](int i) { return column(A[i], B[i]; }
  U &A, &B;
};

proxy(A, B)[0] = 5;
// or you could be evil, overload ",", and get this syntax
(A, B)[0] = 5;

or some sort of variation

Upvotes: -1

Related Questions