Истинный адрес метода объекта

Я знаю, что могу создавать указатели на методы, и я знаю, что они обычно отличаются от указателей на функции. Преобразование между ними не допускается. Указатели на методы могут содержать много данных о том, как настроить this указатель и тд.

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

РЕДАКТИРОВАТЬ:

Почему я не добавляю немного getType() метод, возвращающий тип объекта?

Потому что я хочу использовать таким образом не только созданные мной классы. То, что я пытаюсь сделать, это в основном моя реализацияvariant Класс, который будет принимать практически все, при условии, что существует специализация void* getVariantId() для данного типа.

Тогда я мог бы написать:

template <typename T>
class getTypeId{
};

class foo{
    public:
    void bar();
};

// getTypeId declared but not defined for generic type T earlier...
template <>
class getTypeId<foo>{
    static void* get<foo>(){
        return &foo::bar;
    }
};

void doSomething(myVariant var);

int main(int argc, char* argv[]){
     foo f;
     myVariant var = myVariant::fromType<foo*>(&f);
     doSomething(f);
}

void doSomething(myVariant var){
    foo* f = var.toType<foo*>(); // Returns foo object from main
    int* i = var.toType<int*>(); // returns null, as var is not int* in my example.
}

Идея в том, что fromType использует getTypeId чтобы получить значение, представляющее тип, и указать его вместе с указателем объекта, приведенным к void*, toType с другой стороны, сравнивается значение, полученное из `getTypeId::get, и значение, хранящееся в объекте - если оно совпадает, то внутренний указатель на объект переинтерпретируется обратно в исходный тип и возвращается.

Прелесть этого решения в том, что даже если есть какая-то общая библиотека X, которая определяет тип x, то библиотеки Y и Z, которые отдельно используют библиотеку X, согласятся с тем, что тип x является одинаковым (например, в случае, если вариант create в Y равен передается в Z), адрес причины в методе X:: остается неизменным. Как создатель библиотеки Y или Z (но НЕ X!!!), я не должен гарантировать, что X lib поддерживает RTTI или даже знает о существовании myVariant, хотя тип x по-прежнему полностью совместим с myVariant.

EDIT2:

Теперь я вижу, что не существует независимого от реализации способа сделать это. Однако я думаю, что это не совсем необходимая, а потому и несуществующая функция, а не функция, которую невозможно создать. Я думаю, что я до сих пор не дал понять, что я хотел достичь и как я планировал это сделать. Так:

  • Под библиотеками я подразумеваю здесь общие библиотеки (.dll, .so и так далее).

  • Каждый метод, который не является чисто виртуальным (означает, что каждый определенный метод) должен иметь реализацию. Эта реализация ведет себя как функция, которая принимает один дополнительный параметр this - виртуальные функции отличаются только на стороне вызывающего. Давайте возьмем класс X и его метод X::foo В качестве примера. Этот метод связан только с типом X, даже если тип Y наследует его, этот метод остается X::foo и, следовательно, достаточно для идентификации типа X, Переопределение виртуального метода или метода покрытия в дочернем классе Xchild де-факто определяет новый метод Xchild:: foo с новым адресом. Тогда вы можете использовать Xchild::foo представлять Xchild, но нет X,

  • Если тип X (все его методы должны быть точными) определены в библиотеке libX, а libY и libZ используют libX (но не знают друг друга), и для своих целей определим getTypeId, используя первый метод в X для его представления (обратите внимание, что это ничего не навязывает разработчику libX), тогда они согласны с типом X. Если разработчик приложения использует libY для получения варианта и передачи его в libZ, оба будут правильно распознавать указанный тип.

  • Я НЕ пытаюсь разработать вариант приведения типов - вариант, сохраненный как X, может быть прочитан только как X, даже если фактическим указателем, переданным myVariant, был Xchild. Я не заинтересован в получении типа самого верхнего класса для данного экземпляра - просто класс, который использовался для создания варианта.

3 ответа

Решение

C++ FAQ Lite посвящает целый раздел указателям на функции-члены.

В частности, два ответа указывают, почему то, что вы пытаетесь сделать, не может быть сделано (по крайней мере, не переносимым способом):

  • Функция указателя на член - это другое животное:

C++ вводит новый тип указателя, называемый указатель на член, который может быть вызван только путем предоставления объекта. Не пытайтесь "привести" указатель на функцию-член в указатель на функцию; результат не определен и, вероятно, катастрофический. Например, указатель на функцию-член не обязательно должен содержать машинный адрес соответствующей функции.

  • Функции указателя на член могут указывать на виртуальные функции:

Указатель на функцию-член может быть структурой данных, а не отдельным указателем. Подумайте об этом: если он указывает на виртуальную функцию, он может на самом деле не указывать на статически разрешимую кучу кода, поэтому он может даже не быть обычным адресом.

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

RTTI не ограничен единицами перевода. Единственным ограничением RTTI для библиотек является граница DLL, где одна DLL будет предоставлять функцию, которая возвращает производный класс. И я не уверен на 100%, но пока DLL и исполняемый файл построены с использованием одного и того же компилятора (и совместно используют статические библиотеки), я думаю, что он все еще будет работать.

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

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

Нет никакого способа сделать из этого уникальный идентификатор типа.

Мне кажется, что вам нужен Boost.Any, который использует RTTI для предотвращения неправильного приведения типов.


Места, где ваш план рушится

Вы предлагаете getTypeId функция, которая будет реализована так:

template<typename T>
SomeIdentifierTypedef getTypeId(const T &x)
{
  void *ptr = GetMemberFuncPtr<T>(x, &x::foo);
  return FindIdFromPtr(ptr);
}

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

Хорошо, теперь рассмотрим, что вы сказали:

Давайте возьмем класс X и его метод X::foo в качестве примера. Этот метод связан только с типом X, даже если тип Y наследует его, этот метод остается X::foo и, следовательно, достаточно для идентификации типа X.

Если Y::foo а также X::foo те же функции-члены, то, естественно, они имеют одинаковый указатель на функцию. Следовательно, getTypeId(Y()) == getTypeId(X()), Это нарушает заявленную цель getTypeId, Вы хотите, чтобы он возвращал другое значение, если тип Y, а не X.

Если тип X (все его методы должны быть точными) определены в библиотеке libX, а libY и libZ используют libX (но не знают друг друга), и для своих целей определим getTypeId, используя первый метод в X для его представления (обратите внимание, что это ничего не навязывает разработчику libX), тогда они согласны с типом X. Если разработчик приложения использует libY для получения варианта и передачи его в libZ, оба будут правильно распознавать указанный тип.

Это не может работать по двум причинам. Во-первых, обратите внимание, что моя реализация getTypeId имеет ::foo в этом. Это потому, что нет никакого способа говорить о произвольном методе класса. Вы должны использовать имя (и, вероятно, полноценную подпись, если хотите быть уверенным), чтобы говорить о конкретной функции. Вы не можете просто сказать: "Дай мне какую-нибудь функцию от этого типа". Вы должны выбрать один.

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

О, вы можете попробовать специализировать его для определенных типов. Но это идет вразрез со второй проблемой.

Общие библиотеки не разделяют функции, если они не являются общими. Вы не можете поделиться X::foo, Единственный способ для libX пройти libY класс, который libY не имеет полного и отдельного определения, если класс каким-то образом скрыт. Так что либо непрозрачный указатель (void*), в этом случае вы даже не можете получить функции-члены. Или это производный класс от определения, что оба libX а также libY иметь.

То есть существует некоторый (возможно, абстрактный) класс base, который как libX а также libY были составлены против. libX создает X который получен из baseи есть некоторая функция, которая libX выставляет, который возвращает X как base *,

В этом случае специализация не помогает, потому что только libX имеет доступ к специализации getTypeId<X>, Единственный способ для этого будет, если специализация была для getTypeId<base>, Но затем мы снова сталкиваемся с проблемой: что если пользователь не переопределит виртуальную функцию? Тогда мы думаем, что это все еще base,

Даже если тестовая функция является чисто виртуальной, это не поможет. Это потому что libX может иметь Y который получен из X который получен из base, X возможно, реализовали функцию, но Y не переопределил это. Следовательно getTypeId(x) == getTypeId(y) снова.

Ваша идея не может работать.

Если вы хотите выводить типы только друг от друга (поэтому, в основном, создавайте пользовательские RTTI), вы можете просто создать метод, возвращающий некоторый идентификатор типа.

class MyFirst
{
public:
    inline virtual string GetType() { return "Myfirst"; }
};

Затем перегрузите функцию GetType() для каждого создаваемого вами класса.

Обычно это самый простой метод (кроме, конечно, от dynamic_cast). Если вы ДЕЙСТВИТЕЛЬНО хотите получить тип объекта из виртуального вызова, вы можете установить статический флаг, обозначающий последний использованный тип, или вернуть его из каждой функции, которую имеет указанный объект.

Если бы вы могли написать, КАК вы действительно хотите "получить реальный адрес кода, выполняющего данный метод", ваш вопрос, несомненно, будет более понятным.

Другие вопросы по тегам