«Автоматический» класс прокси в C ++ - PullRequest
2 голосов
/ 11 мая 2010

Мне нужно разрешить пользователю изменять элементы двух структур данных одного типа одновременно. Например:

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}

У меня есть класс, который работает и который сначала меняет a1, а затем копирует a1 в a2. Это хорошо, если:

  • скопированный класс мал
  • пользователь не заботится обо всем копируемом, не только об измененной части.

Есть ли способ получить такое поведение:

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

Я открыт альтернативному синтаксису, но они должны оставаться простыми, и я бы хотел избежать таких вещей, как:

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

или что-либо, связанное с явным указанием &Foo::

[Изменить]

Я должен быть более точным. Дело в том, чтобы работать с библиотекой графов. Библиотека работает с ориентированным графом, но ее использование требует, чтобы при заданных двух вершинах v1 и v2 было ребро v1-> v2, тогда будет ребро v2-> v1. И эти два ребра имеют очень часто (но не всегда) одинаковые свойства. Таким образом, текущая реализация теперь позволяет:

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)

И я хотел бы, чтобы в обозначении подразумевалось, что только a изменено.

Ответы [ 5 ]

2 голосов
/ 11 мая 2010

// чтобы получить желаемый синтаксис

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 =

Это можно улучшить, сделав класс A_Property_Proxy повторно используемым путем параметризации по типу свойства и получения ссылок на свойства вместо ссылок на контейнеры свойств (в данном случае ребра)

   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;
   }
2 голосов
/ 11 мая 2010

Относительно простое решение с 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);
}

Расширение функции dual () с помощью шаблонов переменных / Boost.Preprocessor shenanigans оставлено читателю в качестве упражнения.

0 голосов
/ 12 мая 2010

Путем введения неправильного использования шаблонов я могу получить большую часть желаемого синтаксиса. Это компилируется и работает, но никаких гарантий не дается. Это требует добавления некоторых макросов для использования структуры и использования set_ * вместо прямого присваивания.

#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;
}
0 голосов
/ 11 мая 2010

Изменить (поместите это здесь, потому что комментарии не имеют форматирования)

Так вы говорите, что в вашем текущем коде много такого:

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

И совсем немного этого:

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

А ваши цели [1] - упростить поиск в этих особых случаях [2] менее подробного кода?

----- оригинальный ответ, может быть неактуальным сейчас -----

Вот общая идея, существует множество возможных улучшений:

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

Использование:

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.

Это семантически проще в языках, которые поддерживают свойства, такие как C #. Там окончательный синтаксис будет:

mb.a = 5;
0 голосов
/ 11 мая 2010
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;

или какое-то изменение

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...