Есть ли какая-то возможность сделать так(метакод):
// headerclass A
{
public:
void Copy(const A& a);
private:
// meta-code: want to copy this in Copy methodint iVal_{};
// just ptr, do nothingfloat* fPtr_{};
};
// bodyvoid A::Copy(const A& a)
{
iVal_ = a.iVal_; // i got this code, ok!
//fPtr_ = a.fPtr_; - no code, i don't ask for it
}
Пояснения: у меня что-то типа базы данных. Есть класс, который читает объекты из этой условной базы, это такой мета-класс для описаний абстрактных объектов. Там очень много параметров, и что важно, они постоянно там добавляются-удаляются, в зависимости от требований проекта.
А есть классы, объекты которых инициализируются по описанию из этого мета-класса, и тут все эти классы делают выборку из параметров, типа:
select erw, dgf, oer, osd from metaclass where id = id
Грубо говоря, они по id-у находят объект в стоке и просто копируют нужные для себя поля.
И эти прикладные классы (их переменные) ещё активнее создаются и модифицируются, в зависимости от задач.
И у них есть тот самый метод Copy, который копирует из объекта-описания нужные данные.
Собственно, вопрос в том, что не хочется каждый раз, когда добавляется какой-то элемент в класс, идти писать его копирование. Это, конечно, дисциплинирует, но было пару раз, когда я забывал это сделать.
Можно ли что-то сделать, чтобы при описании переменной в классе указать, чтобы нагенерился код в копире? Один способ с помощью адовой шаблонной магии я знаю, но вот менять int val_ на что-то громоздкое не хочется, хочется иметь на руках именно переменную нужного типа и не лезть в неё геттерами-сеттерами, простота написания и доступа для меня критична.
Несколько лет назад такая ситуация была с конструкторами: добавил переменную, но забыл её обнулить. Я разбирал одно время старый код на работе и много такого видел. А теперь есть:
int val_{some};
которое решает эту проблему почти полностью. Я не успеваю смотреть все новые вещи в плюсах, может уже и тут что-то появилось? Даже было бы хорошо иметь assert на момент компиляции(java @NonNull вспомнилось), если я забыл положить переменную в ксерокс
Здравствуйте, CEMb, Вы писали:
CEM>Собственно, вопрос в том, что не хочется каждый раз, когда добавляется какой-то элемент в класс, идти писать его копирование.
// headerclass A
{
public:
void Copy(const A& a);
private:
struct ToCopy
{
// meta-code: want to copy this in Copy methodint iVal_{};
} c;
struct NoCopy
{
// just ptr, do nothingfloat* fPtr_{};
} n;
};
Здравствуйте, CEMb, Вы писали:
CEM>Собственно, вопрос в том, что не хочется каждый раз, когда добавляется какой-то элемент в класс, идти писать его копирование. Это, конечно, дисциплинирует, но было пару раз, когда я забывал это сделать. CEM>Можно ли что-то сделать, чтобы при описании переменной в классе указать, чтобы нагенерился код в копире? Один способ с помощью адовой шаблонной магии я знаю, но вот менять int val_ на что-то громоздкое не хочется, хочется иметь на руках именно переменную нужного типа и не лезть в неё геттерами-сеттерами, простота написания и доступа для меня критична.
По-моему, это несложно решается и без метапрограммирования. Просто все поля, которые должны копироваться(перемещаться), вносишь в состав безымянной структуры. Пишешь конструктор(ы) копирования(перемещения) и оператор(ы) присваивания, в которых манипулируешь структрурой в целом, а не каждым полем в отдельности. А те поля, которые не должны копироваться(перемещаться) оставляешь непосредственно в классе. После этого, при добавлении нового поля, остается лишь поместить его в правильное место — в класс, либо в структуру:
Я бы сгруппировал свойства для копирования в отдельную структуру без методов.
В своём классе заводишь одно свойство, которое будешь копировать. Остальные свойства будут отдельно.
Это называется инкапсулирование.
Потом в своих классах в методе Copy() или ещё где просто пишешь this->m_props = other.m_props;
class A {
struct props { int a, b, c; };
props m_props;
omg wtf; // остальные свойстваpublic:
void Copy(const A & other) { m_props = other.m_props; }
};
Можно обобщить шаблонами, если таких классов много.
template <typename T>
struct with_copy {
using target_pointer = T *;
target_pointer target() { return static_cast<target_pointer>(this); }
void Copy(const T & other) { target()->m_props = other.m_props; }
};
struct A : public with_copy<A> {
struct props { int a, b, c; };
props m_props;
// прочие свойства
};
struct B : public with_copy<B> {
struct props { double x, y; };
props m_props;
// прочие свойства
};
Вариант 2.
Свой класс наследуешь от структуры со свойствами для копирования. В Copy() делаешь static_cast на базу для вызова оператора присвоения.
В этом случае меньше точек при доступе к свойствам, в отличии от варианта с инкапсуляцией.
struct props_A { /* ... */ };
class A : public props_A {
data some_data; // ну ты понял)
props_A & props_only() { return *static_cast<props_A *>(this); }
void Copy(const A & other) { props_only() = other.props_only(); }
};
Здравствуйте, SomeOne_TT, Вы писали:
R>> void Copy(const A& a) { *this = a; }
SO_>Давненько не писал на плюсах, похоже, забыл многое — как это присваивание работает?
*this — это lvalue выражение, доступное для присваивания. Работает так же, как если бы мы написали:
A& t = *this;
t = a;
P.S. Оператор присваивания здесь может быть как определенным пользователем, так и автоматически сгенерированным компилятором, это уже не суть.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, SomeOne_TT, Вы писали:
R>>> void Copy(const A& a) { *this = a; }
R>P.S. Оператор присваивания здесь может быть как определенным пользователем, так и автоматически сгенерированным компилятором, это уже не суть.
А, т.е. обычный оператор работает, ну да, спасибо.
Тут есть одна фундаментальная проблема для идеального решения: как правильно сделать описание полей? В той же яве это вроде можно через аннотации.
Вторая (моя) проблема для решения через структуру: у поля группируются по смыслу, это удобно для разработки, и совсем не хочется их делить по признаку copyable и выносить в отдельные структуры, к тому же к ним придётся для доступа писать ещё отдельный код (хотя с т.з. генерируемого кода разницы нет) — это третья проблемка.
Поиском нашёл ещё вот такое вот интересное решение, на основе препроцессора boost.PP.
Самое забавное, даже если у нас будет решение вида:
int iVal_; // do: place it in Copy()
это всё равно не спасает программиста от забывания добавить этот мета-код
К тому же есть ситуации, когда не весь "копируемый" контент надо копировать. А ещё есть директивы в описании, которые указывают копировать свои (или родительские) определённые части описания. Не сильно часто, но используется, и решение со структурами внутри класса тут не подходит.
Всем спасибо ещё раз, буду думать, как теперь с этим жить