указатель на под-член
От: tyomitch-cs  
Дата: 12.12.09 19:14
Оценка:
Здравствуйте.

У меня есть, к примеру:

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
Если, по логике вещей, указатель на член -- это просто смещение от начала структуры, то это должно быть возможно, разве нет?
pointer to member
Re: указатель на под-член
От: rg45 СССР  
Дата: 12.12.09 20:49
Оценка:
Здравствуйте, 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, и не может быть использован для обращения к членам ни других типов, ни других классов.

Если хочешь получить дельную подсказку, опиши задачу немного более верхнего уровня.
--
Справедливость выше закона. А человечность выше справедливости.
Re[2]: указатель на под-член
От: tyomitch-cs  
Дата: 12.12.09 21:33
Оценка:
Здравствуйте, 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.
Имеется желание применить этот шаблон к структуре по её подчлену.
Re: указатель на под-член
От: Caracrist https://1pwd.org/
Дата: 12.12.09 22:25
Оценка:
Здравствуйте, 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;

~~~~~
~lol~~
~~~ Single Password Solution
Re[2]: указатель на под-член
От: Caracrist https://1pwd.org/
Дата: 13.12.09 05:51
Оценка:
Здравствуйте, 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>>Если, по логике вещей, указатель на член -- это просто смещение от начала структуры, то это должно быть возможно, разве нет?


C>
C>template <typename T1, typename T2>
C>struct super_caster
C>{
C>    union result
C>    {
C>        T1 var1;
C>        T2 var2;
C>    };
C>};
C>super_caster<int B::*, size_t> sc;
C>sc.result::var2 = offsetof(B2,z)+offsetof(A2,x);
C>int B::* p = sc.result::var1;
C>

C>
можно даже вот так


template <typename T1, typename T2, int assert>
struct super_caster;
template <typename T1, typename T2>
struct super_caster<T1, T2, 0>
{
    union result
    {
        T1 var1;
        T2 var2;
    };
};
template <typename T1, typename T2>
void super_cast(const T1 & from, T2 & to)
{
    super_caster<T1,T2,sizeof(T1) - sizeof(T2)> temp;
    temp.result::var1 = from;
    to = temp.result::var2;
}

// гдето там...
    int B2::* p;
    super_cast(offsetof(B2,z)+offsetof(A2,x), p);

~~~~~
~lol~~
~~~ Single Password Solution
Re[3]: указатель на под-член
От: Caracrist https://1pwd.org/
Дата: 13.12.09 06:03
Оценка:
C>template <typename T1, typename T2, int assert>
C>struct super_caster;
C>template <typename T1, typename T2>
C>struct super_caster<T1, T2, 0>
C>{
C> union result
C> {
C> T1 var1;
C> T2 var2;
C> };
C>};
C>template <typename T1, typename T2>
C>void super_cast(const T1 & from, T2 & to)
C>{
C> super_caster<T1,T2,sizeof(T1) — sizeof(T2)> temp;
C> temp.result::var1 = from;
C> to = temp.result::var2;
C>}

C>// гдето там...

C> int B2::* p;
C> super_cast(offsetof(B2,z)+offsetof(A2,x), p);
C>[/ccode]
C>

А вот это уже мне интересно

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.


VS2008, есть идеи?
~~~~~
~lol~~
~~~ Single Password Solution
Re[4]: указатель на под-член
От: Caracrist https://1pwd.org/
Дата: 13.12.09 06:19
Оценка:
template <typename T1, typename T2>
struct super_caster<T1, T2, 0>
{
    union result
    {
        T1 var1;
        T2 var2;
    };
    char placeHolder[sizeof(T1)]; 
};

Похоже union вообще места не занимает, это не должно быть по большему из типов?
~~~~~
~lol~~
~~~ Single Password Solution
Re[5]: указатель на под-член
От: Caracrist https://1pwd.org/
Дата: 13.12.09 06:27
Оценка:
Блин вот я прогнал

template <typename T1, typename T2, int assert>
struct super_caster;
template <typename T1, typename T2>
struct super_caster<T1, T2, 0>
{
    union result
    {
        T2 value;
    };
    T1 input;
};
template <typename T1, typename T2>
void super_cast(const T1 & from, T2 & to)
{
    super_caster<T1,T2,sizeof(T1) - sizeof(T2)> temp = {from};
    to = temp.result::value;
}

~~~~~
~lol~~
~~~ Single Password Solution
Re[3]: указатель на под-член
От: rg45 СССР  
Дата: 13.12.09 08:10
Оценка:
Здравствуйте, tyomitch-cs, Вы писали:

TC>На данном этапе, меня бы даже устроил совет, как реализовать это через reinterpret_cast, лишь бы заработало.


R>>Если хочешь получить дельную подсказку, опиши задачу немного более верхнего уровня.


То, что я сейчас напишу вряд ли можно дельной подсказкой. Более правильное название этому — грязный хак. Но ты сам просил об этом. Итак, решаем задачу в первоначальной постановке, которая она была сделана здесь
Автор: tyomitch-cs
Дата: 12.12.09
.

Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать:
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
}


Надо заметить, что задачу, поставленную тобой здесь
Автор: tyomitch-cs
Дата: 13.12.09
, решить будет сложнее, если вообще получится. Проблема в том, что трюки с реинтерпретацией указателей на члены здесь нужно будет проделать над константами времени компиляции. Как это сделать я пока еще не придумал. Склоняюсь к мысли, что это невозможно.
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: указатель на под-член
От: rg45 СССР  
Дата: 13.12.09 08:36
Оценка:
Здравствуйте, rg45, Вы писали:

R>Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать:

R>
R>template<typename T, typename O1, typename O2>
R>T O1::* superposition(O2 O1::* mem, T O2::* sub);
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);
--
Справедливость выше закона. А человечность выше справедливости.
Re: указатель на под-член
От: tyomitch  
Дата: 16.12.09 12:12
Оценка: 1 (1)
Коллеги, возник ещё один вопрос по теме.
Как сделать указатель на "невидимый член" базового класса?
К примеру:

struct A { float w; int x; } ;
struct B { char y; } ;
struct C : public A, public B { } ;
B C::* p;
C c;


И цель — чтобы c.*p было эквивалентно (B)c

Опять же, кажется, что можно изхитриться и узнать смещение предка в потомке, а по нему сконструировать указатель?
Re[4]: указатель на под-член
От: Pavel Dvorkin Россия  
Дата: 16.12.09 13:05
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>Для начала определим обобщенную функцию, которая по двум указателям — на член и на подчлен — возвращает их "суперпозицию", так сказать:


М-да. Как говорится, зачем делать просто, если можно сделать сложно. Никак в этой задаче не обойтись без template, специальной функции и двух десятков строк...

Или все же можно ? Я попробовал, получилось вроде не хуже.

int mem = offsetof(B,z) + offsetof(A,x);
int B::* p = (int B::*&)(mem);
  B b;
  b.*p = 123;
With best regards
Pavel Dvorkin
Re[2]: указатель на под-член
От: Pavel Dvorkin Россия  
Дата: 16.12.09 13:49
Оценка:
Здравствуйте, 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.
With best regards
Pavel Dvorkin
Re[5]: указатель на под-член
От: XuMuK Россия  
Дата: 16.12.09 14:50
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>
PD>int mem = offsetof(B,z) + offsetof(A,x);
PD>


http://cplusplus.com/reference/clibrary/cstddef/offsetof/ :

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).

Re[5]: указатель на под-член
От: rg45 СССР  
Дата: 16.12.09 16:55
Оценка: +1 :)
Здравствуйте, 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>



--
Справедливость выше закона. А человечность выше справедливости.
Re[6]: указатель на под-член
От: Pavel Dvorkin Россия  
Дата: 17.12.09 06:07
Оценка: :)
Здравствуйте, 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 будет, конечно, вынесена в хедер, а хедеры никто не смотрит. В итоге... если что-то потом окажется не так, придется разбираться намного дольше. У меня же все ясно и недвусмысленно сказано.
  • With best regards
    Pavel Dvorkin
    Re[6]: указатель на под-член
    От: Pavel Dvorkin Россия  
    Дата: 17.12.09 06:19
    Оценка:
    Здравствуйте, XuMuK, Вы писали:

    XMK>Здравствуйте, Pavel Dvorkin, Вы писали:


    PD>>
    PD>>int mem = offsetof(B,z) + offsetof(A,x);
    PD>>


    XMK>http://cplusplus.com/reference/clibrary/cstddef/offsetof/ :

    XMK>

    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).


    Это само собой подразумевалось, что речь идет о структурах С, а не о классах с виртуальностями и т.д.
    With best regards
    Pavel Dvorkin
    Re[3]: указатель на под-член
    От: tyomitch  
    Дата: 17.12.09 13:23
    Оценка:
    Здравствуйте, 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::*&); ради такого преобразования предыдущим отписавшимся в треде и пришлось городить чёрти-что из шаблонов и юнионов.

    Спасибо.
    Re[4]: указатель на под-член
    От: Caracrist https://1pwd.org/
    Дата: 17.12.09 14:20
    Оценка:
    Здравствуйте, tyomitch, Вы писали:


    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 битных указателях, захватит несколько неожиданный кусочек памяти...
    ~~~~~
    ~lol~~
    ~~~ Single Password Solution
    Re[4]: указатель на под-член
    От: Pavel Dvorkin Россия  
    Дата: 18.12.09 03:41
    Оценка:
    Здравствуйте, tyomitch, Вы писали:

    Мне одно не совсем ясно. Зачем тебе это нужно ? Какая польза от того, что ты можешь написать

    C.*p = чему-то ?

    Сделать так, чтобы p мог показывать сейчас на одно, а потом на другое ? Так не проще ли завести вместо этой структуры массив из этих "одно и другое" (они же одного типа!)и добираться через банальный char* ? Как-то я плохо себе представляю ситуацию, когда надо ходить по структуре, показывая на разные по смыслу поля с помощью одного указателя... Я понимаю — указатель на функцию-член, но на данные ?
    Ну уж и не говорю о том. что как только в этой структуре появится что-то виртуальное, так сразу все это накроется медным тазом. Да и вопросы переносимости под сомнением. И из того, что это работает под VC++, не следует, что это вполне соответствует страндарту — на этот вопрос, кстати. так никто и не ответил.
    With best regards
    Pavel Dvorkin
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.