есть задача обращаться к полям класса по их имени, т.е. зная строку. Это строка, приходит, парсится потом собственно идет само обращение.
Каждое поля класса это класс, интерфейс которого нам известен.
Придумал вот такую функцию, которую вызываю из констактора и заполняю мап со смещениями для всех полей:
bool ORMtableBase::FillFiledsMap(FieldsList& fields, const ORMtableBase* _this)
{
size_t Finish = _this->GetSize(); // size of main class
size_t Start = _this->GetFirstField(); // offset to first field
while(Start < Finish)
{
const IORMField* pField =
static_cast<const IORMField*> // common interface for each field
(
static_cast<const void*>
(
(
static_cast<const char*>
(
static_cast<const void*>(_this)
) + Start
)
)
);
fields.insert(std::pair<std::string, FieldInfo>(pField->GetName(), FieldInfo(pField->GetType(), Start))); // sometimes it crashes here
Start += pField->GetSize(); // sizeof(ClassFieldName)
}
return !fields.empty();
}
и все замечательно работает на 32-х машинах, но на 64-х битных возникает крэш, ровно для одного из типов, который возвращает размер 42, а реально смещение до следующего поля 46. Выравниваение во всех проектах стоит 8 байт. Это похоже на проблему выравнивания и можно было ббы написать функцию, которая бы подравнивала такие значения, вот только будет ли это гарантированно правильно во всех компиляторах?
P.S. Все эти извращения потому, что в месте использования нет информации о конечном типе, а есть только интерфейс.
Здравствуйте, dilmah, Вы писали:
D>я не понимаю, ты хочешь сказать что offsetof неправильно работает?
он работает правильно, но я не могу его использовать для всех членов класса. Могу только для первого.
Я же хотел сказать что offsetof != sizeof() для некоторых полей, sizeof() == 42, а оffsetof 46 из-за выравнивания.
S>это работает, но как-то это не по-казачьи. Может кто-то предложет что-то получше?
1. 'offsetof' корректно(гарантированно) работает для POD
2. почему не использовать указатели(враперы для указателей) на члены класса для обращения к таковым?
т.к. Ваша реализация(вычисления) вероятно сильно будет зависить от реализации/настроек компилятора,
и/или не учитывает #pragma pack's, а у Вас не видно использований 'alignof'(MSVC '__alignof'). И т.п.
Если считать( ) то более корректно с учетом ниже сказанного.
2.a) заменить "8" на что-то типо:
struct someStruct {};
int opt=alignof(someStruct); // get compiler option 'alignment'
2.b) учесть выравнивание самого класса(влияние #pragma pack's)
3.
Здравствуйте, szag, Вы писали:
S>P.S. Все эти извращения потому, что в месте использования нет информации о конечном типе, а есть только интерфейс.
Странная какая-то затея. Чем тебе offsetof() не нравится? Собственно, никто никому не обещает, что сумма размеров членов даст в результате правильное смещение следующего члена. Тут ещё куча вопросов по ходу дела, например, зачем обращаться по смещению (!), одновременно скрывая информацию о классе? Чтоб спрашивали, что ли? Давай, ты лучше про саму задачу расскажешь.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, szag, Вы писали:
S>это работает, но как-то это не по-казачьи. Может кто-то предложет что-то получше?
Не надо так делать, ты вносишь дополнительные зависимости, которые очень сложно отлавливать. Расскажи про саму задачу. Почти наверняка есть какой-то удобоваримый способ решения, без колдунства.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, szag, Вы писали:
[skipped]
S>P.S. Все эти извращения потому, что в месте использования нет информации о конечном типе, а есть только интерфейс.
Я думаю, — решение можно представить в виде регистратора(статического) объектов(рожденных от class Object)
Примерно так:
//------------------------------------------------------------------------------
class Object {
public:
Object();
virtual ~Object() = 0;
string getName();
static Object* findObjectByName(const string &name);
/*
* Здесь Ваш "известный" интерфейс, вероятно virtual some_result_type1(-N) some_fun1(-N)(arg_list1-(-N)) = 0;
*/
protected:
typedef map<string, Object*> c_objReg;
typedef c_objReg::iterator c_objRegI;
// регистр объектов
static c_objReg objReg;
// имя
string name;
// запрос на регистрацию объекта с конкретным именем
bool registerName(const string &in_name);
};
//------------------------------------------------------------------------------
Object::c_objReg Object::objReg=Object::c_objReg();
//------------------------------------------------------------------------------
Object::Object() {}
//------------------------------------------------------------------------------
Object::~Object() {}
//------------------------------------------------------------------------------
string Object::getName() {
return name; // или typeid(*this).name()
}
//------------------------------------------------------------------------------
Object* Object::findObjectByName(const string &name) {
return objReg[name];
}
//------------------------------------------------------------------------------
bool Object::registerName(const string &in_name) {
if (in_name.size() && objReg.insert(make_pair(in_name, this)).second)
name=in_name;
return true;
}
Соответственно в конструкторах производных классов сделать registerName(typeid(*this).name()); или произвольное(удобное) имя
class foo1: public Object {
public:
foo1() { registerName("Object_foo1"); }
};
class foo2: public Object {
public:
foo2() { registerName("Object_foo2"); }
};
Найти его: Object::findByName(<desired_object_name>);
Здравствуйте, Seigram, Вы писали:
S>Я думаю, — решение можно представить в виде регистратора(статического) объектов(рожденных от class Object)
я думал о чем-то подобном, однако такой подход имеет ряд недостатков и накладывает определенные обязанности на пользователя библиотеки, чего не хотелось бы.
Библиотека представляет из себя либу + хидер. На вход получает класс унаследованный от определенного, заранее известного интерфейса, либа заправшивает данные и они приходят в виде xml. потом она заполняет поля класса значениями и возвращает true. Все возможные типы полей заранее известны, точнее известны типы данных хранящихся там. Сами поля представляют из себя сборку класса из нескольких темплейтов, в зависимости от того что ввел пользователь библиотеки, так как поля содержат всякую информацию, к примеру для строк максимальную длинну. таких полей в классе может быть от нескольких штук до нескольких десятков и ничего с этим не поделаешь, если так пожелает пользователь. Поэтому не хотелось бы делать вариант с регистрацией и заставлять пользователя регистрировать все имена полей. По условиям задачи в классе не может быть лишних, системных, не нужных и прочих полей — все именно поля необходимые для заполнения, поэтому не хотелось бы давать лишнюю гибкость пользователю.
Сейчас это сделано с помощью макроса, который собирает класс из темплейтов и создает поле, а пользователю достаточно написать:
FIELD(MyName, 50, INITVALUE("Ivan"))
Соответственно создастся поле, с максимальной длинной 50 и по умолчанию будет инициализировано как "Ivan". Вещей, подобных INITVALUE, может быть несколько (есть обязательные и не обязательные), все примеры приводить не буду, но думаю общий смысл понятен. Сейчас все эти вещи проверяются на этапе компиляции и удобны для пользователя библиотеки.
Здравствуйте, szag, Вы писали:
S>Здравствуйте, Seigram, Вы писали:
S>>Я думаю, — решение можно представить в виде регистратора(статического) объектов(рожденных от class Object)
S>я думал о чем-то подобном, однако такой подход имеет ряд недостатков и накладывает определенные обязанности на пользователя библиотеки, чего не хотелось бы. S>Библиотека представляет из себя либу + хидер. На вход получает класс унаследованный от определенного, заранее известного интерфейса, либа заправшивает данные и они приходят в виде xml. потом она заполняет поля класса значениями и возвращает true. Все возможные типы полей заранее известны, точнее известны типы данных хранящихся там. Сами поля представляют из себя сборку класса из нескольких темплейтов, в зависимости от того что ввел пользователь библиотеки, так как поля содержат всякую информацию, к примеру для строк максимальную длинну. таких полей в классе может быть от нескольких штук до нескольких десятков и ничего с этим не поделаешь, если так пожелает пользователь. Поэтому не хотелось бы делать вариант с регистрацией и заставлять пользователя регистрировать все имена полей. По условиям задачи в классе не может быть лишних, системных, не нужных и прочих полей — все именно поля необходимые для заполнения, поэтому не хотелось бы давать лишнюю гибкость пользователю. S>Сейчас это сделано с помощью макроса, который собирает класс из темплейтов и создает поле, а пользователю достаточно написать: S>
S>FIELD(MyName, 50, INITVALUE("Ivan"))
S>
S>Соответственно создастся поле, с максимальной длинной 50 и по умолчанию будет инициализировано как "Ivan". Вещей, подобных INITVALUE, может быть несколько (есть обязательные и не обязательные), все примеры приводить не буду, но думаю общий смысл понятен. Сейчас все эти вещи проверяются на этапе компиляции и удобны для пользователя библиотеки.
Чесно? -Объяснение не понятное. И что такое "сборку класса из нескольких темплейтов" — агрегация или наследование используется?
Далее, про вышесказанное "колдунство" с формулами, — при данных условиях(вернее ограничениях), по видимому без него не обойтись, но необходимо знать как компилятор размещает в памяти члены класса. И 1-е на чем ты накололся это выравнивания членов класса, т.е.
как правило(если члены не char's все) то sum[sizeof(member[i]] != sizeof(структура их содержащая).
Потому что(цитирую): "структура выравнивается на максимальное выравнивание среди полей. Каждое поле выравнивается на выравнивание типа поля".
И если ты хочешь правильного колдунства, то должен это учесть; как я уже сказал ответами выше есть оператор alignof()/__alignof() который это разрешает узнавать, и — offsetof корректен только для POD типов.
В каком то смысле ты должен имитировать функцию компилятора отвечающую за вычесление размещения.
Просто интересно, а зачем выравнивание если вокруг него(именно в данной ситуации) возникает столько проблем. Можно охватить все pack(push,1)|pack(pop) тогда очевидно даже 1-й код приведенный автором "сойдется".
Здравствуйте, Seigram, Вы писали:
S> Чесно? -Объяснение не понятное. И что такое "сборку класса из нескольких темплейтов" — агрегация или наследование используется?
Используется и агрегация и наследование (но это не важно), в конечном коде это будет выглядеть так (привожу упрощенный пример):
class MyClass : public iClassBase
{
public:
int i1, i2, i3; // 3 поля гарантированные для этого класса
std::size_t GetFirstField() const
{
return offsetof(MyClass, i3) + sizeof(int);
}
std::size_t GetSize() const
{
return sizeof(MyClass);
}
// ниже идут поля классаclass MyField : public MyFieldBase
{
public:
MyField() {/*здесь будет код*/}
std::size_t GetSize() const
{
return sizeof(MyField) + MyAlign<MyField>::Result;
}
};
MyField Fild1Name;
/* и так далее для каждого поля */
};
S>Далее, про вышесказанное "колдунство" с формулами, — при данных условиях(вернее ограничениях), по видимому без него не обойтись, но необходимо знать как компилятор размещает в памяти члены класса. И 1-е на чем ты накололся это выравнивания членов класса, т.е. S>как правило(если члены не char's все) то sum[sizeof(member[i]] != sizeof(структура их содержащая).
Я с этим согласен, поэтому и спросил совета — как лучше сделать.
S>Потому что(цитирую): "структура выравнивается на максимальное выравнивание среди полей. Каждое поле выравнивается на выравнивание типа поля". S>И если ты хочешь правильного колдунства, то должен это учесть; как я уже сказал ответами выше есть оператор alignof()/__alignof() который это разрешает узнавать, и — offsetof корректен только для POD типов. S>В каком то смысле ты должен имитировать функцию компилятора отвечающую за вычесление размещения.
Да, вы правы насчет POD типов, но, к сожалению, у меня они не POD и с этим сложно что-либо поделать. Мне необходимо чтобы этот код можно было использовать под виндами 32/64 бит и *nix 32/64. Поэтому необходима совместимость с гсс. И конечно хотелось бы чтобы была гарантия со стороны стандарта.
Здравствуйте, Тролль зеленый и толстый, Вы писали:
ТЗИ>Используй указатели на члены класса, а не трюки с offset'ов. У меня в одном проекте подобная задача — это, фактически, рефлексия для C++.
я бы рад, но, к сожалению, это невозможно в моей задаче.
Здравствуйте, szag, Вы писали:
S>Пока придумал вот такую конструкцию и проверил на всех смещениях от 1 до 16 — работает. S>Может кто-то покритикует или предложет что-то получше?