Определить экземпляр класса члена в C

#c

#c

Вопрос:

Я никогда не видел этого ни на одном языке, но мне было интересно, возможно ли это с помощью какого-нибудь трюка, которого я не знаю.

Допустим, у меня есть функция, подобная

 struct A {
  // some members and methods ...
  some_t t;
  // more members ...
};   


void test(some_tamp; x) { // a reference to avoid copying a new some_t
    // obtain the A instance if x is the t member of an A 
    // or throw an error if x is not the t member of an A
    ...
    // do something
}
  

Можно ли было бы получить экземпляр, A чьим членом t является x ?

Комментарии:

1. Что вы пытаетесь сделать? Вероятно, вы задаете неправильный вопрос.

2. получить его откуда? пустота? не понимаю, что вы пытаетесь сделать, но то, о чем вы просите, не только невозможно, но я не думаю, что это имеет слишком большой смысл

3. Невозможно, если все A не зарегистрированы в словаре. Как иначе мы узнаем, что все some_t являются членами A? Некоторые из них могут быть частью B!

4. Вы можете (вроде как) Я опубликовал ответ, но удалил его, потому что он не соответствовал бы всем вашим требованиям. Учитывая указатель на переменную-член, вы можете выяснить, что такое указатель экземпляра, но если то, что вам дано, на самом деле не было указателем на переменную-член, вы не смогли бы его обнаружить и получить бессмысленный указатель. Как говорили другие, если об этом спрашивают не из академического любопытства, вы делаете что-то очень, очень неправильное.

5. решение idz было, по сути, реализацией на c container_of макроса, предложенного другим, и у него были те же недостатки.

Ответ №1:

Нет, к сожалению, это невозможно.

Комментарии:

1. Есть очень мало вещей, которые компилятор C не позволит вам сделать, если вы попросите использовать достаточное количество приведений.

2. @Ben И, применяя эти приведения, вы почти наверняка вызываете неопределенное или неуказанное поведение.

Ответ №2:

Если вы знаете, что у вас есть ссылка на t элемент некоторого A экземпляра, вы можете получить экземпляр с помощью container_of , например A* pa = container_of(amp;x, A, t); .

Проверка того, что результирующий указатель действительно является A , технически возможна тогда и только тогда, A когда имеет виртуальные члены, к сожалению, нет переносимого метода для проверки.

Однако вы можете достичь чего-то подобного, используя множественное наследование и dynamic_cast , что позволяет выполнять перекрестное приведение между подобъектами.

Комментарии:

1. О! Я не ожидал, что он существует, позвольте мне это проверить

2. container_of был ли добавлен в стандарт C ? Круто! Когда это произошло?

3. @Neil: Это легко реализуется с помощью offsetof , который является частью стандарта C , поэтому ваши саркастические бредни довольно бессмысленны. Хотя стоит отметить, что результаты offsetof [и, следовательно, container_of ] четко определены в C только в том случае, A если это POD.

4. @Dennis Я склонен придерживаться здесь общего взгляда. Если бы OP сказал «могу ли я для модуля определить, является ли элемент экземпляром», тогда я мог бы сказать «да». Но он этого не сделал. структуры используются в этом теге как сокращение для классов со всеми общедоступными членами, но (возможно) с виртуальными функциями, и это то, о чем, я думал, он спрашивал. И ничто не меняет того факта, что container_of не является (и никогда не будет) частью C .

5. @Neil: Вот почему я предоставил ссылку на определение.

Ответ №3:

Вы можете добавить указатель на A inside some_t (конечно, если some_t есть struct или class )

вот так:

 struct some_t
{
  A *a;
  ...
};

void test(some_tamp; x) 
{
  if( x.a )
  {
    // do some
  }
  else
    throw ...
}
  

Комментарии:

1. Есть ли какой-нибудь глубоко невежественный человек, который скрывается в тегах c , голосуя за подобные ответы? Это, безусловно, кажется так. Если это описывает вас — пожалуйста, остановитесь сейчас.

2. Вы хотели бы несколько конкретизировать реализацию, но это действительно может сработать. Создайте a const и только установите для него ненулевое значение в частном конструкторе, доступном только из A . Это было бы ненадежным и, вероятно, неправильным способом решения любой проблемы, с которой сталкивается OP, но это сделало бы свое дело.

3. Я действительно понимаю, чего хочет YuppieNetworking. В данном конкретном случае это сработает. Конечно, он не может проверить, является ли x членом другого класса.

Ответ №4:

Если вы можете изменить структуру A и ее конструктор и если вы можете обеспечить упаковку структуры, вы можете добавить значение непосредственно после t, которое содержит некоторый волшебный ключ.

 struct A {
  ...
  some_t t
  struct magic_t
  { 
    uint32 code
    some_t* pt;
  } magic;
}
#define MAGICCODE 0xC0DEC0DE //or something else unique 
  

В конструкторе A выполните:
this->magic.code = MAGICCODE; this->magic.pt = amp;(this->t);
Затем вы можете написать

 bool test(some_t *t)  //note `*` not `amp;`
{
    struct magic_t* pm = (struct magic_t*)(t 1);
    return (pm->pt == t amp;amp; pm->code == MAGICCODE);
}
  

Ответ №5:

Этот ответ не соответствует всем требованиям исходного вопроса, я удалил его, но OP попросил меня опубликовать его. Это показывает, как при очень специфических условиях вы можете вычислить указатель экземпляра из указателя на переменную-член.

Вы не должны, но вы можете:

 #include <iostream>
#include <cstddef>

using namespace std;

struct A
{
    int x;
    int y;
};

struct A* find_A_ptr_from_y(int* y)
{
    int o = offsetof(struct A, y);
    return (struct A*)((char *)y - o);
}

int main(int argc, const char* argv[])
{
    struct A a1;
    struct A* a2 = new struct A;

    cout << "Address of a1 is " << amp;a1 << endl;
    cout << "Address of a2 is " << a2 << endl;

    struct A *pa1 = find_A_ptr_from_y(amp;a1.y);
    struct A *pa2 = find_A_ptr_from_y(amp;(a2->y));

    cout << "Address of a1 (recovered) is " << pa1 << endl;
    cout << "Address of a2 (recovered) is " << pa2 << endl;
}
  

Вывод

 Address of a1 is 0x7fff5fbff9d0
Address of a2 is 0x100100080
Address of a1 (recovered) is 0x7fff5fbff9d0
Address of a2 (recovered) is 0x100100080
  

Предостережения: если то, что вы передаете find_A_ptr_from_y , не является указателем на (struct A).y, вы получите полный мусор.

Вы не должны (почти) никогда этого делать. Смотрите комментарий DasBoot ниже.

Комментарии:

1. Спасибо за повторную публикацию, это те же результаты, которые я получаю с container_of : действительно, если указатель не указывает на элемент, результат будет мусорным.

2. Я даже не знал об container_of .

3. Как и во многих утверждениях, которые говорят, что вы никогда не должны что-либо делать, я думаю, что на самом деле существует множество исключений. Если вы инкапсулируете его в хорошо известный макрос (как это делает ядро Linux с помощью container_of() ), то его использование довольно безопасно и может сэкономить много бесполезных дополнительных указателей.

4. @DasBoot это очень верно! Я сам использовал это в драйверах устройств (вот как я узнал об этом). Возможно, мне следовало сказать, если вы действительно не знаете, что делаете .

Ответ №6:

Мне не совсем ясно, что вы пытаетесь сделать, но если вы хотите найти указатель на экземпляр struct A, когда вы знаете указатель на элемент A, вы можете это сделать.

Смотрите, например, макрос container_of в ядре Linux.

Ответ №7:

Параметр x функции test() не обязательно должен быть членом какого-либо класса, насколько test() преобразуется.

Если семантически в конкретном приложении x всегда должен быть член класса, то эта информация может быть предоставлена либо путем передачи дополнительного параметра, либо some_t сама по себе содержать такую информацию. Однако делать это было бы совершенно излишне, поскольку, если test() действительно необходим доступ к объекту, содержащему x , то почему бы просто не передать сам родительский объект? Или просто создать test() функцию-член того же класса и вообще не передавать параметры? Если причина в том, что x может принадлежать разным классам, то для решения этой проблемы можно использовать полиморфизм.

В принципе, я полагаю, что нет ситуации, когда вам понадобилась бы такая возможность, которая не могла бы быть решена более простым, безопасным и более объектно-ориентированным способом.