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);
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать:
М-да. Как говорится, зачем делать просто, если можно сделать сложно. Никак в этой задаче не обойтись без template, специальной функции и двух десятков строк...
Или все же можно ? Я попробовал, получилось вроде не хуже.
int mem = offsetof(B,z) + offsetof(A,x);
int B::* p = (int B::*&)(mem);
B b;
b.*p = 123;
Здравствуйте, 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).
Здравствуйте, 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>Ну, во-первых, я конечно и сам люблю иногда загнуть, но в данном случае перебор: строк всего шесть, не считая двух фигурных скобок, а не пара десятков, как ты пишешь.
Ладно, перебрал.
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 будет, конечно, вынесена в хедер, а хедеры никто не смотрит. В итоге... если что-то потом окажется не так, придется разбираться намного дольше. У меня же все ясно и недвусмысленно сказано.
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++, не следует, что это вполне соответствует страндарту — на этот вопрос, кстати. так никто и не ответил.