views:

151

answers:

5

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:

  • the class copied is small
  • the user doesn't mind about everything being copied, not only the part modified.

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.

A: 
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

aaa
A: 

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;
egrunin
actually, you are making copies, rather than storing reference
aaa
Fixed, I think...
egrunin
The problem here is that you need to make a whole new proxy for each class ... and you need to define a new method per property to set.
PierreBdR
You can't have a vector of references in the first place (pointers would be fine, though).
visitor
@Pierre: yes, that's why I said you could make this into a templated class (which is a lot more code, and is basically doing by hand what a Propertied language does for you).
egrunin
@visitor: Yikes! good catch.
egrunin
+2  A: 

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.

scjohnno
+1  A: 

//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;
   }
Alsk
That's an interesting part of the problem that's dealt with. Thanks.
PierreBdR
A: 

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;
}
Winston Ewert