Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, rg45, Вы писали:
R>>Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать:
PD>М-да. Как говорится, зачем делать просто, если можно сделать сложно. Никак в этой задаче не обойтись без template, специальной функции и двух десятков строк...
PD>Или все же можно ? Я попробовал, получилось вроде не хуже.
PD>
PD>int mem = offsetof(B,z) + offsetof(A,x);
PD>int B::* p = (int B::*&)(mem);
PD> B b;
PD> b.*p = 123;
PD>
Ну, во-первых, я конечно и сам люблю иногда загнуть, но в данном случае перебор: строк всего шесть, не считая двух фигурных скобок, а не пара десятков, как ты пишешь.
Во-вторых, эти шесть строк написаны один раз на все случаи жизни. Если хочешь сравнить, что проще, то честнее будет сравнивать простоту использования, а не реализации. А тут ты явно проигрываешь. Смотри сам, что проще и легче для восприятия:
int B::* p = superposition(&B::z, &A::x);
или
int mem = offsetof(B,z) + offsetof(A,x);
int B::* p = (int B::*&)(mem);
???
Вдобавок, для вспомогательной переменной mem ты всякий раз будешь придумывать уникальное имя или будешь использовать одну на все случаи жизни? Это тоже к вопросу о простоте использования.
В-третьих, в твоем решении применяется макрос
В-четвертых, и это самое главное. Я с самого начала своего поста сказал, что данное решение — это хак. Только в моем решении этот хак тщательно локализован и спрятан под капот. Вызывающий код, не смотря на то, что он пользуется этим хаком чист как младенец. Ты же предлагаешь, эту нечисть размазать по всему клиентскому коду и тем самым подложить свинью тому, кому, не дай бог, прийдется ЭТО сопровождать.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать:
Извините, что влезаю в дискуссию... А что, если с самого начала избавиться от такого явления, как указатель на член?
Указатель на член — это особая функция, на вход которой подают ссылку на объект, а на выходе — ссылка на другой объект.
Сделаем из него не особую, а обычную функцию.
template<class Host, class Member, Member Host::*mp>
Member& member(Host& host) { return host.*mp; }
// суперпозиция во время компиляцииtemplate<class A, class B, class C, B&(*a2b)(A&), C&(*b2c)(B&)>
C& superposition(A& a) { return b2c(a2b(a)); }
struct P { int x, y; };
struct Q { P z; int t; };
int& (*get_t)(Q&) = member<Q,int,&Q::t>;
int& (*get_x)(Q&) = superposition<Q,P,int, member<Q,P,&Q::z>, member<P,int,&P::x> >;
Q q = { { 1, 2 }, 3 };
int& t = get_t(q);
int& x = get_x(q);
Здесь получилось нечто многословное, с явным указанием типов всех участников.
Можно подумать, как допилить до автоматического вывода. Возможно, завернуть в шаблоны классов...
И кстати, указатели на члены-данные прекрасно замыкаются в boost::bind / boost::function.
Понятно, что оверхед и всё такое, но может, у компилятора хватит ума проинлайнить — чтобы bind(&P::x, bind(&Q::z, _1))(q) превратилось в q.*(&Q::z).*(&P::x) — а оттуда в q.z.x ?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Тем более, что твоя superposition будет, конечно, вынесена в хедер, а хедеры никто не смотрит.
Выделенный тезис представляется мне более чем сомнительным...
PD>В итоге... если что-то потом окажется не так, придется разбираться намного дольше. У меня же все ясно и недвусмысленно сказано.
Фишка вы том, что
1) в superposition можно организовать, вообще-то контроль типов.
2) можно организовать тестирование работоспособности этого хака, чтобы на платформе, где всё сломается, сразу и не собралось бы или не запустилось и т. д.
3) (на самом деле главное), когда понадобится переносить этот код на платформу, где хак будет немного другой (например, будут хранить в указателе на поле не смещение, а смещение + константа), то в варианте с superposition понадобится сделать условную компиляцию в одном месте, а у тебя всюду в клиентском коде. И объём правок, конечно разный.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
PD>Когда ты последний раз заглядывал в stdio.h ?
Ну он, во-первых, стандартный, и, во-вторых, я им вроде бы не пользуюсь к тому же...
PD>>>В итоге... если что-то потом окажется не так, придется разбираться намного дольше. У меня же все ясно и недвусмысленно сказано.
Если написать superposition достаточно аккуратно, то сразу будет понятно где разбираться...
PD>В общем, со многим можно согласиться, но лучше вообще это нигде не писать и не провоцировать, делая вид, что superposition — это честная функция, а не злобный хак
Ну так ясно, что без хака лучше
Но мы сравнивали не вариант с хаком и вариант без, а твой с superposition...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, rg45, Вы писали:
R>Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать:
М-да. Как говорится, зачем делать просто, если можно сделать сложно. Никак в этой задаче не обойтись без template, специальной функции и двух десятков строк...
Или все же можно ? Я попробовал, получилось вроде не хуже.
int mem = offsetof(B,z) + offsetof(A,x);
int B::* p = (int B::*&)(mem);
B b;
b.*p = 123;
Здравствуйте, rg45, Вы писали:
R>Ну, во-первых, я конечно и сам люблю иногда загнуть, но в данном случае перебор: строк всего шесть, не считая двух фигурных скобок, а не пара десятков, как ты пишешь.
Ладно, перебрал.
R>Во-вторых, эти шесть строк написаны один раз на все случаи жизни. Если хочешь сравнить, что проще, то честнее будет сравнивать простоту использования, а не реализации. А тут ты явно проигрываешь. Смотри сам, что проще и легче для восприятия: R>
R>int B::* p = superposition(&B::z, &A::x);
R>
R>или R>
R>int mem = offsetof(B,z) + offsetof(A,x);
R>int B::* p = (int B::*&)(mem);
R>
R>???
Я так думаю, что все же второе. По той простой причине, что в нем честно говорится, что тут делается. В то время как в твоем решении надо еще пробиться через эти 3 template и понять, что чему соответствует.
R>Вдобавок, для вспомогательной переменной mem ты всякий раз будешь придумывать уникальное имя или будешь использовать одну на все случаи жизни? Это тоже к вопросу о простоте использования.
Вообще-то интересный вопрос. Не совсем, правда, понятно, почему именно тут он у тебя возникает. Его можно и более в общем плане поставить — если нужна вспомогательная переменная для чего бы то ни было, будем пользоваться каждый раз одной и той же или же каждый раз надо заводить новую ? . Философский вопрос, что и говорить.
R>В-третьих, в твоем решении применяется макрос
И что ? Ничего плохого я в этом не вижу.
R>В-четвертых, и это самое главное. Я с самого начала своего поста сказал, что данное решение — это хак. Только в моем решении этот хак тщательно локализован и спрятан под капот. Вызывающий код, не смотря на то, что он пользуется этим хаком чист как младенец. Ты же предлагаешь, эту нечисть размазать по всему клиентскому коду и тем самым подложить свинью тому, кому, не дай бог, прийдется ЭТО сопровождать.
Ну-ну. То, что хак — согласен. Но хак, упрятанный под капот, ничем не лучше хака, который явно виден. Скорее наоборот, он говорит явно — внимание, это хак! Твой же вызывающий код, чистый аки новорожденный младенец, создает у меня ложное впечатление, что хака и нет вообще. Тем более, что твоя superposition будет, конечно, вынесена в хедер, а хедеры никто не смотрит. В итоге... если что-то потом окажется не так, придется разбираться намного дольше. У меня же все ясно и недвусмысленно сказано.
struct A { float w; int x; } ;
struct B { char y; A z; } ;
int B::* p;
B b;
Как мне теперь в переменную p запихнуть адрес поля B::z.x?
Так, чтобы b.*p было эквивалентно b.z.x
Если, по логике вещей, указатель на член -- это просто смещение от начала структуры, то это должно быть возможно, разве нет?
Здравствуйте, tyomitch-cs, Вы писали:
TC>Здравствуйте.
TC>У меня есть, к примеру:
TC>
TC>struct A { float w; int x; } ;
TC>struct B { char y; A z; } ;
TC>int B::* p;
TC>B b;
TC>
TC>Как мне теперь в переменную p запихнуть адрес поля B::z.x? TC>Так, чтобы b.*p было эквивалентно b.z.x
Как-то чересчур уж жестко вопрос поставлен. Цель поставлена четко: чтобы было эквивалентно b.z.x. Форма выражения задана строго: b.*p. Определение p тоже не двусмысленно: int B::* p. Никакой степени свободы. В такой постановке вопроса ответ один: никак.
TC>Если, по логике вещей, указатель на член -- это просто смещение от начала структуры, то это должно быть возможно, разве нет?
Внутреннее устройство данных и то, как они расположены в памяти, в данном случае не имеют никакого значения, мало ли что с чем совпадает во внутреннем представлении. Здесь все упирается в семантику объявлений и выражений — int B::* обозначает: член класса B типа int, и не может быть использован для обращения к членам ни других типов, ни других классов.
Если хочешь получить дельную подсказку, опиши задачу немного более верхнего уровня.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Внутреннее устройство данных и то, как они расположены в памяти, в данном случае не имеют никакого значения, мало ли что с чем совпадает во внутреннем представлении. Здесь все упирается в семантику объявлений и выражений — int B::* обозначает: член класса B типа int, и не может быть использован для обращения к членам ни других типов, ни других классов.
Тип именно int. Хочу обратиться к элементу класса B типа int; единственная загвоздка, что он не напрямую член, а как-бы-подчлен.
На данном этапе, меня бы даже устроил совет, как реализовать это через reinterpret_cast, лишь бы заработало.
R>Если хочешь получить дельную подсказку, опиши задачу немного более верхнего уровня.
Имеется template <class T, class K, K T::* pk> class Index, добавляющий объект типа T в индекс по полю pk типа K.
Имеется желание применить этот шаблон к структуре по её подчлену.
Здравствуйте, tyomitch-cs, Вы писали:
TC>Здравствуйте.
TC>У меня есть, к примеру:
TC>
TC>struct A { float w; int x; } ;
TC>struct B { char y; A z; } ;
TC>int B::* p;
TC>B b;
TC>
TC>Как мне теперь в переменную p запихнуть адрес поля B::z.x? TC>Так, чтобы b.*p было эквивалентно b.z.x TC>Если, по логике вещей, указатель на член -- это просто смещение от начала структуры, то это должно быть возможно, разве нет?
template <typename T1, typename T2>
struct super_caster
{
union result
{
T1 var1;
T2 var2;
};
};
super_caster<int B::*, size_t> sc;
sc.result::var2 = offsetof(B2,z)+offsetof(A2,x);
int B::* p = sc.result::var1;
Здравствуйте, Caracrist, Вы писали:
C>Здравствуйте, tyomitch-cs, Вы писали:
TC>>Здравствуйте.
TC>>У меня есть, к примеру:
TC>>
TC>>struct A { float w; int x; } ;
TC>>struct B { char y; A z; } ;
TC>>int B::* p;
TC>>B b;
TC>>
TC>>Как мне теперь в переменную p запихнуть адрес поля B::z.x? TC>>Так, чтобы b.*p было эквивалентно b.z.x TC>>Если, по логике вещей, указатель на член -- это просто смещение от начала структуры, то это должно быть возможно, разве нет?
template <typename T1, typename T2>
struct super_caster<T1, T2, 0>
{
union result
{
T1 var1;
T2 var2;
};
char placeHolder[4];//Если не добавлять эту строчку, то он выжаёт ошибку при выходе из super_cast
};
Run-Time Check Failure #2 — Stack around the variable 'temp' was corrupted.
Здравствуйте, tyomitch-cs, Вы писали:
TC>На данном этапе, меня бы даже устроил совет, как реализовать это через reinterpret_cast, лишь бы заработало.
R>>Если хочешь получить дельную подсказку, опиши задачу немного более верхнего уровня.
То, что я сейчас напишу вряд ли можно дельной подсказкой. Более правильное название этому — грязный хак. Но ты сам просил об этом. Итак, решаем задачу в первоначальной постановке, которая она была сделана здесь
Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать:
template<typename T, typename O1, typename O2>
T O1::* superposition(O2 O1::* mem, T O2::* sub)
{
int memValue = reinterpret_cast<int&>(mem);
int subValue = reinterpret_cast<int&>(sub);
int value = memValue + subValue;
return reinterpret_cast<T O1::*&>(value);
}
Теперь применим "инструмент" к данному тобой примеру:
#include <iostream>
struct A { float w; int x; };
struct B { char y; A z; };
int B::* p = superposition(&B::z, &A::x);
int main()
{
B b;
b.*p = 123;
std::cout << b.z.x << std::endl; //Output: 123
}
Надо заметить, что задачу, поставленную тобой здесь
, решить будет сложнее, если вообще получится. Проблема в том, что трюки с реинтерпретацией указателей на члены здесь нужно будет проделать над константами времени компиляции. Как это сделать я пока еще не придумал. Склоняюсь к мысли, что это невозможно.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать: R>
С помощью этой штуки можно "нырять" на любую глубину:
struct V { int value; };
struct W { V v; };
struct X { W w; };
struct Y { X x; };
struct Z { Y y; };
int Z::* imem = superposition(superposition(superposition(superposition(&Z::y, &Y::x), &X::w), &W::v), &V::value);
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, tyomitch, Вы писали:
T>Коллеги, возник ещё один вопрос по теме. T>Как сделать указатель на "невидимый член" базового класса? T>К примеру:
T>
T>struct A { float w; int x; } ;
T>struct B { char y; } ;
T>struct C : public A, public B { } ;
T>B C::* p;
T>C c;
T>
T>И цель — чтобы c.*p было эквивалентно (B)c
T>Опять же, кажется, что можно изхитриться и узнать смещение предка в потомке, а по нему сконструировать указатель?
Можно, конечно. Я специально слегка усложнил — переставил местами родителей
#include"stdafx.h"#include"stddef.h"struct A { float w; int x; } ;
struct B { char y; } ;
struct C : public B, public A { } ;
int _tmain(int argc, _TCHAR* argv[])
{
// хочу доступ к C::A::x
C c;
B* pB = (B*)&c;
A* pA = (A*)&c;
int offsetAinC = (char*) pA - (char*) pB; // а вот в этом быть уверенным я не могу ИМХО. Или могу ? Знатоки стандарта, ответьте!int mem = offsetAinC + offsetof(A,x) ;
int C::* p = (int C::*&) mem;
c.*p = 123;
return 0;
}
Но вообще — безобразие все это. Грязный хак, вполне согласен с rg45.
Because of the extended functionality of structs in C++, in this language, the use of offsetof is restricted to "POD types", which for classes, more or less corresponds to the C concept of struct (although non-derived classes with only public non-virtual member functions and with no constructor and/or destructor would also qualify as POD).
XMK>Because of the extended functionality of structs in C++, in this language, the use of offsetof is restricted to "POD types", which for classes, more or less corresponds to the C concept of struct (although non-derived classes with only public non-virtual member functions and with no constructor and/or destructor would also qualify as POD).
Это само собой подразумевалось, что речь идет о структурах С, а не о классах с виртуальностями и т.д.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Можно, конечно. Я специально слегка усложнил — переставил местами родителей
PD>
PD>#include"stdafx.h"
PD>#include"stddef.h"
PD>struct A { float w; int x; } ;
PD>struct B { char y; } ;
PD>struct C : public B, public A { } ;
PD>int _tmain(int argc, _TCHAR* argv[])
PD>{
PD> // хочу доступ к C::A::x
PD> C c;
PD> B* pB = (B*)&c;
PD> A* pA = (A*)&c;
PD> int offsetAinC = (char*) pA - (char*) pB; // а вот в этом быть уверенным я не могу ИМХО. Или могу ? Знатоки стандарта, ответьте!
PD> int mem = offsetAinC + offsetof(A,x) ;
PD> int C::* p = (int C::*&) mem;
PD> c.*p = 123;
PD> return 0;
PD>}
PD>
PD>Но вообще — безобразие все это. Грязный хак, вполне согласен с rg45.
Не совсем то, что я имел в виду (мне нужен был C::A, а не C::A::x), но идея понятна.
Кстати, в gcc (и вроде бы стандартом тоже) запрещены касты из int в (int C::*&); ради такого преобразования предыдущим отписавшимся в треде и пришлось городить чёрти-что из шаблонов и юнионов.
T>Кстати, в gcc (и вроде бы стандартом тоже) запрещены касты из int в (int C::*&); ради такого преобразования предыдущим отписавшимся в треде и пришлось городить чёрти-что из шаблонов и юнионов.
И правильно! Я как и все остальные тут создавал UB кастя в int и size_t
Использовать можно только intptr_t или ptrdiff_t,
иначе:
int mem = offsetAinC + offsetof(A,x) ;
int C::* p = (int C::*&) mem
на 64 битных указателях, захватит несколько неожиданный кусочек памяти...
Мне одно не совсем ясно. Зачем тебе это нужно ? Какая польза от того, что ты можешь написать
C.*p = чему-то ?
Сделать так, чтобы p мог показывать сейчас на одно, а потом на другое ? Так не проще ли завести вместо этой структуры массив из этих "одно и другое" (они же одного типа!)и добираться через банальный char* ? Как-то я плохо себе представляю ситуацию, когда надо ходить по структуре, показывая на разные по смыслу поля с помощью одного указателя... Я понимаю — указатель на функцию-член, но на данные ?
Ну уж и не говорю о том. что как только в этой структуре появится что-то виртуальное, так сразу все это накроется медным тазом. Да и вопросы переносимости под сомнением. И из того, что это работает под VC++, не следует, что это вполне соответствует страндарту — на этот вопрос, кстати. так никто и не ответил.
Здравствуйте, Erop, Вы писали:
PD>>Тем более, что твоя superposition будет, конечно, вынесена в хедер, а хедеры никто не смотрит. E>Выделенный тезис представляется мне более чем сомнительным...
Когда ты последний раз заглядывал в stdio.h ?
PD>>В итоге... если что-то потом окажется не так, придется разбираться намного дольше. У меня же все ясно и недвусмысленно сказано.
E>Фишка вы том, что E>1) в superposition можно организовать, вообще-то контроль типов. E>2) можно организовать тестирование работоспособности этого хака, чтобы на платформе, где всё сломается, сразу и не собралось бы или не запустилось и т. д. E>3) (на самом деле главное), когда понадобится переносить этот код на платформу, где хак будет немного другой (например, будут хранить в указателе на поле не смещение, а смещение + константа), то в варианте с superposition понадобится сделать условную компиляцию в одном месте, а у тебя всюду в клиентском коде. И объём правок, конечно разный.
В общем, со многим можно согласиться, но лучше вообще это нигде не писать и не провоцировать, делая вид, что superposition — это честная функция, а не злобный хак
Здравствуйте, tyomitch, Вы писали:
T>Коллеги, возник ещё один вопрос по теме. T>Как сделать указатель на "невидимый член" базового класса? T>К примеру:
T>
T>struct A { float w; int x; } ;
T>struct B { char y; } ;
T>struct C : public A, public B { } ;
T>B C::* p;
T>C c;
T>
T>И цель — чтобы c.*p было эквивалентно (B)c
T>Опять же, кажется, что можно изхитриться и узнать смещение предка в потомке, а по нему сконструировать указатель?
Ну и я отмечусь:
template<typename Base, typename Derived>
Base Derived::* reinterpret_base_as_member()
{
int derived_ = 123; //Что угодно, но не 0!
Derived* derived = reinterpret_cast<Derived*&>(derived_);
Base* base = derived;
int base_ = reinterpret_cast<int&>(base);
int offset = base_ - derived_;
Base Derived::* member = reinterpret_cast<Base Derived::*&>(offset);
return member;
}
Использовать так:
#include <iostream>
struct A { int a; };
struct B { int b; };
struct AB : A, B { };
B AB::* m = reinterpret_base_as_member<B, AB>();
int main()
{
AB t;
(t.*m).b = 123;
std::cout << t.b << std::endl; //Output: 123
}
Хочется обратить внимание на присутствие контроля типов. Если попытаться вызвать reinterpret_base_as_member для типов, не являющихся предком и потомком, то будет ошибка компиляции. Также контроль типов присутствует и здесь
К>// суперпозиция во время компиляции
К>template<class A, class B, class C, B&(*a2b)(A&), C&(*b2c)(B&)>
К>C& superposition(A& a) { return b2c(a2b(a)); }
К>int& (*get_x)(Q&) = superposition<Q,P,int, member<Q,P,&Q::z>, member<P,int,&P::x> >;
К>
Да, в этом месте очень не хватает автоматического выведения пареметров шаблонной функции по параметрам же шаблона. Наподобие того, как параметры шаблона выводятся из параметров функции.
Казалось бы, чего бы не перенести указатели на функции из списка параметров шаблона в список параметров функции? А тут есть один тонкий момент, который не всеми сходу улавливается. А между тем, разница в возможностях использования указателей на функции, находящихся в списке параметров шаблона, и указателей в списке параметров функции огромная. Пока указатель на функцию находится в параметрах шаблона он является константой времени компиляции, и на его основе можно определить другую функцию и получить указатель другого типа, и тоже во время компиляции! Таким образом, получается эдакий механизм конвертации указателей на функции. При переносе указателя на функцию из параметров шаблона в параметры функции эта замечательная возможность теряется.
Лет пять назад я делал приспособление, которое долгое время было предметом моей гордости. Оно позволяло перенести-таки указатели на функции из параметров шаблона в параметры функции, и при этом не потерять описанные выше преимущества! При этом вызовы функций, подобных superposition обходились без этих огромных списков шаблонных параметров и стали выглядеть гораздо компактнее, а самое главное, перестали быть чувствительны к изменениям в сигнатурах целевых функций! В основе решения лежал грязнейший хак, гораздо более грязный, чем все вместе взятые, мелькавшие в этой ветке. В двух словах — из сегмента кода вырезались кусочки исполняемого кода и переносились в буфер, расположенный в динамической памяти. Потом еще выполнялась кое-какая дополнительная инициализация этих блочков. После этого этим кусочкам кода в нужный момент передавалось управление. Собственно, эти кусочки и выполняли роль тех самых функций, которые в первом случае можно было генерить совершенно штатным образом. Решение (на MSVC-7.1) было непереносимым, заточенным под 32-битную платформу. Но оно работало и на тот момент все остались чрезвычайно довольны
--
Справедливость выше закона. А человечность выше справедливости.