доступ к полям с помощью шаблона
От: pepsicoca  
Дата: 05.02.13 13:34
Оценка:
Добрый день.

Есть MSVC2010 и есть класс:


class a{

char  field1; 
int   field2;
float field3;

public:

char  get1(){return field1;}
int   get2(){return field2;}
float get3(){return field3;}

//template<class T> T get(){return ???;}

};



Нужен доступ к полям класса.
Каждый раз писать новую функцию getX не хочется.
Хотелось бы применить шаблон.
Но так как имена полей разные, то шаблон применить нельзя.

Вопросы:

1. Как быть и что делать? Неужели нужно писать вручную все функции доступа к полям?

Спасибо.
Re: доступ к полям с помощью шаблона
От: DrDred Россия  
Дата: 05.02.13 13:43
Оценка:
Здравствуйте, pepsicoca, Вы писали:


P>Добрый день.


P>Есть MSVC2010 и есть класс:


[skipped]

P>Нужен доступ к полям класса.

P>Каждый раз писать новую функцию getX не хочется.
P>Хотелось бы применить шаблон.
P>Но так как имена полей разные, то шаблон применить нельзя.

P>Вопросы:


P>1. Как быть и что делать? Неужели нужно писать вручную все функции доступа к полям?


P>Спасибо.


Объявить поля public Или речь идет о создании только RO доступа?
Написать макрос?
--
WBR, Alexander
Re[2]: доступ к полям с помощью шаблона
От: pepsicoca  
Дата: 05.02.13 13:51
Оценка: -4
Здравствуйте, DrDred, Вы писали:

DD>Здравствуйте, pepsicoca, Вы писали:



P>>Добрый день.


P>>Есть MSVC2010 и есть класс:


DD>[skipped]


P>>Нужен доступ к полям класса.

P>>Каждый раз писать новую функцию getX не хочется.
P>>Хотелось бы применить шаблон.
P>>Но так как имена полей разные, то шаблон применить нельзя.

P>>Вопросы:


P>>1. Как быть и что делать? Неужели нужно писать вручную все функции доступа к полям?


P>>Спасибо.


DD>Объявить поля public Или речь идет о создании только RO доступа?


Публичные поля это ересь, так учит нас Страуструп.

DD>Написать макрос?


Макросы это еще большая ересь, так учит нас Страуструп.

А если серьезно, хочется все делать по науке, с проверкой типов компилятором и все такое.
Re: доступ к полям с помощью шаблона
От: Кодт Россия  
Дата: 05.02.13 13:52
Оценка: 12 (1) -1
Здравствуйте, pepsicoca, Вы писали:

P>1. Как быть и что делать? Неужели нужно писать вручную все функции доступа к полям?


Написать на метаязыке (на макросах, короче говоря)
class A
{
  FIELD(char, 1)
  FIELD(int, 2)
  FIELD(float, 3)
};

Реализация элементарная
// склейку токенов лучше брать готовую, чем голый оператор ## и потом удивляться правилам подстановки макроаргументов
#define FIELD_NAME(suffix)  BOOST_PP_CAT(field,suffix)
#define GETTER_NAME(suffix) BOOST_PP_CAT(get,suffix)

#define FIELD(type, suffix) \
  private: \
    type FIELD_NAME(suffix); \
  public: \
    type GETTER_NAME(suffix) const { return FIELD_NAME(suffix); } \
Перекуём баги на фичи!
Re[2]: доступ к полям с помощью шаблона
От: pepsicoca  
Дата: 05.02.13 13:58
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, pepsicoca, Вы писали:


P>>1. Как быть и что делать? Неужели нужно писать вручную все функции доступа к полям?


К>Написать на метаязыке (на макросах, короче говоря)

К>
К>class A
К>{
К>  FIELD(char, 1)
К>  FIELD(int, 2)
К>  FIELD(float, 3)
К>};
К>

К>Реализация элементарная
К>
К>// склейку токенов лучше брать готовую, чем голый оператор ## и потом удивляться правилам подстановки макроаргументов
К>#define FIELD_NAME(suffix)  BOOST_PP_CAT(field,suffix)
К>#define GETTER_NAME(suffix) BOOST_PP_CAT(get,suffix)

К>#define FIELD(type, suffix) \
К>  private: \
К>    type FIELD_NAME(suffix); \
К>  public: \
К>    type GETTER_NAME(suffix) const { return FIELD_NAME(suffix); } \

К>


То есть на шаблонах это сделать нельзя?
Re[3]: доступ к полям с помощью шаблона
От: Кодт Россия  
Дата: 05.02.13 14:28
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>То есть на шаблонах это сделать нельзя?


А как ты это себе представляешь?
Шаблоны не порождают имена, а только используют существующие. То есть, get<int> или get<1> сделать можно, а get_int или get_1 — нельзя.
Склейка токенов — это уровень лексера либо рефлекшена. Лексер — см. препроцессор. Рефлекшен — см. другой язык

Впрочем, можно написать кучу внешних функций вида
template<class T>
auto get_1(T const& t) -> decltype(t.field_1)
{ return t.field_1; }

и может быть, даже подверстать это добро в класс в виде микс-ина
template<class Final> class define_getters
{
  auto get_1() const -> decltype(((Final*)this)->field_1)
  { return ((Final*)this)->field_1; }

  auto get_2() const -> decltype(((Final*)this)->field_2)
  { return ((Final*)this)->field_2; }

  // и т.д. - нагенерить макросами
};

class A : public define_getters<A>
{
  friend class define_getters<A>;
  int field_10;
};

Но это C++11... в С++0x или 98 информацию о типе поля просто так нахаляву не извлечёшь.
Мне рисуются какие-нибудь мрачные решения со списками типов. Уж лучше сразу макросы.
Перекуём баги на фичи!
Re[3]: доступ к полям с помощью шаблона
От: johny5 Новая Зеландия
Дата: 05.02.13 14:36
Оценка: 12 (1)
Здравствуйте, pepsicoca, Вы писали:

P>То есть на шаблонах это сделать нельзя?


Ещё можно сделать вот так:

class A
{
public:
   struct InternalData
   {
      char field1;
      int field2;
      float field3;
   };

   const InternalData&  GetData() const { return data; }

private:
   InternalData  data;
};



И да, чтоб с шаблонами:
   template<typename T>
   typename enable_if< has_key< mpl::set<AllowedAccessor1, AllowedAccessor2>, T>, const InternalData&>::type
      GetData() const { return data; }


Re: доступ к полям с помощью шаблона
От: Abyx Россия  
Дата: 05.02.13 14:49
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>
P>char  field1; 
P>int   field2;
P>float field3;
P>


это называется std::tuple, или boost::fusion, в зависимости от того что вы с этими полями собираетесь делать.
In Zen We Trust
Re[2]: доступ к полям с помощью шаблона
От: Abyx Россия  
Дата: 05.02.13 14:53
Оценка:
Здравствуйте, Кодт, Вы писали:

К>
К>class A
К>{
К>  FIELD(char, 1)
К>  FIELD(int, 2)
К>  FIELD(float, 3)
К>};
К>


жуть какая.
а если их 10 и надо в начало новый добавить — все индексы руками перебивать?
In Zen We Trust
Re[3]: доступ к полям с помощью шаблона
От: Evgeny.Panasyuk Россия  
Дата: 05.02.13 16:05
Оценка: 5 (1)
Здравствуйте, pepsicoca, Вы писали:

DD>>Объявить поля public Или речь идет о создании только RO доступа?

P>Публичные поля это ересь, так учит нас Страуструп.

Кто тебе это сказал?

http://www.artima.com/intv/goldilocks2.html

Bjarne Stroustrup: Going to the big class hierarchy is again, you write more code than you need to, and you get too much connection between different parts. I particularly dislike classes with a lot of get and set functions. That is often an indication that it shouldn't have been a class in the first place. It's just a data structure. And if it really is a data structure, make it a data structure.
My rule of thumb is that you should have a real class with an interface and a hidden representation if and only if you can consider an invariant for the class.

Bill Venners: What do you mean by invariant?

Bjarne Stroustrup: What is it that makes the object a valid object? An invariant allows you to say when the object's representation is good and when it isn't. Take a vector as a very simple example. A vector knows that it has n elements. It has a pointer to n elements. The invariant is exactly that: the pointer points to something, and that something can hold n elements. If it holds n+1 or n-1 elements, that's a bug. If that pointer is zero, it's a bug, because it doesn't point to anything. That means it's a violation of an invariant. So you have to be able to state which objects make sense. Which are good and which are bad. And you can write the interfaces so that they maintain that invariant. That's one way of keeping track that your member functions are reasonable. It's also a way of keeping track of which operations need to be member functions. Operations that don't need to mess with the representation are better done outside the class. So that you get a clean, small interface that you can understand and maintain.

Bill Venners: So the invariant justifies the existence of a class, because the class takes the responsibility for maintaining the invariant.

Bjarne Stroustrup: That's right.

Bill Venners: The invariant is a relationship between different pieces of data in the class.

Bjarne Stroustrup: Yes. If every data can have any value, then it doesn't make much sense to have a class. Take a single data structure that has a name and an address. Any string is a good name, and any string is a good address. If that's what it is, it's a structure. Just call it a struct. Don't have anything private. Don't do anything silly like having a hidden name and address field with get_name and set_address and get_name and set_name functions. Or even worse, make a virtual base class with virtual get_name and set_name functions and so on, and override it with the one and only representation. That's just elaboration. It's not necessary.

Re[3]: доступ к полям с помощью шаблона
От: Кодт Россия  
Дата: 05.02.13 20:13
Оценка:
Здравствуйте, Abyx, Вы писали:

A>жуть какая.

A>а если их 10 и надо в начало новый добавить — все индексы руками перебивать?

А это зависит от того, как раздаются имена.
Если "1", "2", "42", "foo" — то самое оно.
Если строго последовательно, то берём, скажем, BOOST_PP_SEQ_FOR_EACH_I — и рожаем последовательно
#define DECLARE_FIELDS(seq) BOOST_PP_SEQ_FOR_EACH_I(DECLARE_NTH_FIELD, fake_data, seq)
#define DECLARE_NTH_FIELD(r, data, i, elem) DECLARE_FIELD(elem, i)

class A
{
  DECLARE_FIELDS( // синтаксис непривычный, зато не требует variadic macros
    (int)
    (float)
    (char)
  )
};

Вот только много ли смысла от "надо в начало новый добавить"? Это же, скорее всего, приведёт к тотальному кровавому рефакторингу: по всему коду придётся get1 на get2 ... get9 на get10 переправлять.

Но надо заметить, что такое вот централизованное и последовательное объявление полей удобно скрещивается с шаблонным метапрограммированием — т.е. мы можем создать список типов и доступаться к полям не только по именам, созданным лексером — get1, field1, но и вычисленным — get<1>, field<1>.

И кстати сказать, тему легко развить. С помощью макросов мы каждому полю не только индекс дадим, но и произвольный тэг. И доступаться будем не только по индексу — get1, get<1>, но и по тэгу — get<foo>.
Примерно такой дизайн
class A
{
  DECLARE_FIELDS(
    ((int))
    ((float)(foo)) // именованное поле
    ((char)(bar))
    ((long))
  )
// превратится в
public:
  typedef structure<int,float,char,long> fields;
  typedef int2type<2> foo;
  typedef int2type<3> bar;
private:
  fields m_fields;
public:
  template<int I>   typename fields::get_type<I       >::type get_i() const { return m_fields.get_value<I>(); }
  template<class T> typename fields::get_type<T::value>::type get_t() const { return m_fields.get_value<T::value>(); }
};
Перекуём баги на фичи!
Re[4]: доступ к полям с помощью шаблона
От: Abyx Россия  
Дата: 05.02.13 23:00
Оценка:
Здравствуйте, Кодт, Вы писали:

К>class A

К>{
К> DECLARE_FIELDS( // синтаксис непривычный, зато не требует variadic macros
К> (int)
К> (float)
К> (char)
К> )
К>};
К>[/c]
К>Вот только много ли смысла от "надо в начало новый добавить"? Это же, скорее всего, приведёт к тотальному кровавому рефакторингу: по всему коду придётся get1 на get2 ... get9 на get10 переправлять.

если в коде есть токены "get1", "get2" и т.п. автора надо бы пристрелить.

К>Но надо заметить, что такое вот централизованное и последовательное объявление полей удобно скрещивается с шаблонным метапрограммированием — т.е. мы можем создать список типов и доступаться к полям не только по именам, созданным лексером — get1, field1, но и вычисленным — get<1>, field<1>.


круто. только для этого есть std::tuple, уже готовая, и без макросов.

----

а что кстати не так с variadic macros? они вроде во всех основных компиляторах есть
In Zen We Trust
Re[2]: доступ к полям с помощью шаблона
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 06.02.13 09:58
Оценка: -2
Здравствуйте, Abyx, Вы писали:

A>Здравствуйте, pepsicoca, Вы писали:


P>>
P>>char  field1; 
P>>int   field2;
P>>float field3;
P>>


A>это называется std::tuple, или boost::fusion, в зависимости от того что вы с этими полями собираетесь делать.

А это назвается из пушки по воробьям .
Sic luceat lux!
Re[5]: доступ к полям с помощью шаблона
От: Кодт Россия  
Дата: 06.02.13 18:17
Оценка:
Здравствуйте, Abyx, Вы писали:

К>>Вот только много ли смысла от "надо в начало новый добавить"? Это же, скорее всего, приведёт к тотальному кровавому рефакторингу: по всему коду придётся get1 на get2 ... get9 на get10 переправлять.


A>если в коде есть токены "get1", "get2" и т.п. автора надо бы пристрелить.


Предлагаю заменить расстрел пожизненной правкой циферок в списках декларации.

И потом, топикстартер с этого же и начал: с токенов get1, get2... Видать, они там есть.
Но возможно, что эти токены не руками набиваются, а тоже каким-то средством автоматизации, — препроцессором или внешней тулзой.


К>>Но надо заметить, что такое вот централизованное и последовательное объявление полей удобно скрещивается с шаблонным метапрограммированием — т.е. мы можем создать список типов и доступаться к полям не только по именам, созданным лексером — get1, field1, но и вычисленным — get<1>, field<1>.


A>круто. только для этого есть std::tuple, уже готовая, и без макросов.


С помощью макросов можно сделать удобный обвес вокруг tuple.
Я не стал заостряться именно на std::tuple, написав в эскизе абстрактную structure<.....> — как всегда, здесь возможны варианты. Может, топикстартеру потребуется играться со списком типов, и припахать boost::mpl? Откуда я это знаю.


A>а что кстати не так с variadic macros? они вроде во всех основных компиляторах есть


Есть-то они есть, но. Если не хочется рукодельщины, то напрашивается использовать BOOST_PP_VARIADIC_xxxxx, а я не знаю, как свежий (или несвежий) буст поддерживает 2010 студию в этом вопросе. Тогда как SEQ поддерживается стопудов.

Вот чисто для примера, проблема на ровном месте. Ubuntu 12.04 LTS, официальная репа, метапакеты gcc и boost: gcc 4.6.3 (не поддерживает С++11), буст 1.46 (BOOST_PP_VARIADIC в виде заглушки).
Перекуём баги на фичи!
Re[6]: доступ к полям с помощью шаблона
От: Abyx Россия  
Дата: 06.02.13 19:06
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Вот чисто для примера, проблема на ровном месте. Ubuntu 12.04 LTS, официальная репа, метапакеты gcc и boost: gcc 4.6.3 (не поддерживает С++11), буст 1.46 (BOOST_PP_VARIADIC в виде заглушки).

а еще говорят в никсах программировать удобно
In Zen We Trust
Re[7]: доступ к полям с помощью шаблона
От: Кодт Россия  
Дата: 06.02.13 20:38
Оценка:
Здравствуйте, Abyx, Вы писали:

К>>Вот чисто для примера, проблема на ровном месте. Ubuntu 12.04 LTS, официальная репа, метапакеты gcc и boost: gcc 4.6.3 (не поддерживает С++11), буст 1.46 (BOOST_PP_VARIADIC в виде заглушки).

A>а еще говорят в никсах программировать удобно

Угу. Это ж дебьян, те ещё консерваторы.
Вот, думаю, — поставить себе двух гусей (из канонической репы и из свежайшего) и потом следить, чтобы вызывалось то, что надо, а не то, что попало? Или пользоваться онлайн-компиляторами наподобие LWS для разных proof-of-concept, а локально — по старинке, оставатся в рамках gnu++0x.
Перекуём баги на фичи!
Re[4]: доступ к полям с помощью шаблона
От: pepsicoca  
Дата: 07.02.13 07:11
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здравствуйте, pepsicoca, Вы писали:


DD>>>Объявить поля public Или речь идет о создании только RO доступа?

P>>Публичные поля это ересь, так учит нас Страуструп.

EP>Кто тебе это сказал?


EP>http://www.artima.com/intv/goldilocks2.html

EP>

EP>Bjarne Stroustrup: Going to the big class hierarchy is again, you write more code than you need to, and you get too much connection between different parts. I particularly dislike classes with a lot of get and set functions. That is often an indication that it shouldn't have been a class in the first place. It's just a data structure. And if it really is a data structure, make it a data structure.
EP>My rule of thumb is that you should have a real class with an interface and a hidden representation if and only if you can consider an invariant for the class.

EP>Bill Venners: What do you mean by invariant?

EP>Bjarne Stroustrup: What is it that makes the object a valid object? An invariant allows you to say when the object's representation is good and when it isn't. Take a vector as a very simple example. A vector knows that it has n elements. It has a pointer to n elements. The invariant is exactly that: the pointer points to something, and that something can hold n elements. If it holds n+1 or n-1 elements, that's a bug. If that pointer is zero, it's a bug, because it doesn't point to anything. That means it's a violation of an invariant. So you have to be able to state which objects make sense. Which are good and which are bad. And you can write the interfaces so that they maintain that invariant. That's one way of keeping track that your member functions are reasonable. It's also a way of keeping track of which operations need to be member functions. Operations that don't need to mess with the representation are better done outside the class. So that you get a clean, small interface that you can understand and maintain.

EP>Bill Venners: So the invariant justifies the existence of a class, because the class takes the responsibility for maintaining the invariant.

EP>Bjarne Stroustrup: That's right.

EP>Bill Venners: The invariant is a relationship between different pieces of data in the class.

EP>Bjarne Stroustrup: Yes. If every data can have any value, then it doesn't make much sense to have a class. Take a single data structure that has a name and an address. Any string is a good name, and any string is a good address. If that's what it is, it's a structure. Just call it a struct. Don't have anything private. Don't do anything silly like having a hidden name and address field with get_name and set_address and get_name and set_name functions. Or even worse, make a virtual base class with virtual get_name and set_name functions and so on, and override it with the one and only representation. That's just elaboration. It's not necessary.


А раньше он не так говорил. Укатали сивку крутые горки...

На самом деле открытые члены неудобны, так как к ним нельзя обращаться в шаблонах по типу.
Функции get в этом смысле гораздо удобнее и правильнее.
Re: доступ к полям с помощью шаблона
От: pepsicoca  
Дата: 07.02.13 07:53
Оценка:
P>Спасибо.

В продолжении темы: попробовал создать функции get с помощью шаблонов.


template<class T>
class b{
T val;
public:
b(T v):val(v){}
void get(T* v){*v=val;}
};

class a: public b<char>, public b<int>, public b<float> {
public:
a(char c,int i,float f):b<char>(c),b<int>(i),b<float>(f){}
};

main(){

a<char,int,float> aa(1,2,3);

char field1;
int field2;
float field3;
aa.get(&field1);
aa.get(&field2);
aa.get(&field3);
cout<<"\nfield1="<<(int)field1;
cout<<"\nfield2="<<field2;
cout<<"\nfield3="<<field3;
 
}


На такой код MSVC2010 ругается, говорит что конфликт функций get.

Вместе с тем такой код:


class a{

char  field1;
int   field2;
float field3;

public:

a(char c,int i,float f):field1(c),field2(i),field3(f){}

void get(char* v){*v=field1;}
void get(int* v){*v=field2;}
void get(float* v){*v=field3;}

};

int main(int argc,char* argv[]){

a aa(1,2,3);

char field1;
int field2;
float field3;
aa.get(&field1);
aa.get(&field2);
aa.get(&field3);
cout<<"\nfield1="<<(int)field1;
cout<<"\nfield2="<<field2;
cout<<"\nfield3="<<field3;

}


Транслируется.

Вопрос:

1. Почему при автоматической генерации функций get из шаблонов разного типа инстанцирования между ними конфликт, а если те же самые функции get написать руками, то конфликта нет?

Спасибо
Re[2]: доступ к полям с помощью шаблона
От: pepsicoca  
Дата: 07.02.13 09:04
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>>Спасибо.


P>В продолжении темы: попробовал создать функции get с помощью шаблонов.


P>


P>main(){

P>a<char,int,float> aa(1,2,3);



Извиняюсь, тут должно быть


main(){

a aa(1,2,3);


Просто был еще вариант


template<class T1,class T2,class T3>
class a: public b<T1>, public b<T2>, public b<T3> {
public:
a(char c,int i,float f):b<T1>(c),b<T2>(i),b<T3>(f){}
};


Этот вариант тоже не транслируется с диагностикой "конфликт функций get".
Re[5]: доступ к полям с помощью шаблона
От: Evgeny.Panasyuk Россия  
Дата: 07.02.13 09:19
Оценка:
Здравствуйте, pepsicoca, Вы писали:

P>А раньше он не так говорил.


Пруф?

P>На самом деле открытые члены неудобны, так как к ним нельзя обращаться в шаблонах по типу.


"На самом деле руки не удобны, так как на них нельзя поставить самосвал"

P>Функции get в этом смысле гораздо удобнее и правильнее.


Как уже говорили ранее, тебе нужен Boost.Fustion, а в частности set:
typedef set<int, float> S;
S s(12, 5.5f);
std::cout << at_key<int>(s) << std::endl;
std::cout << at_key<float>(s) << std::endl;
std::cout << result_of::has_key<S, double>::value << std::endl;
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.