Как я могу напечатать значение указателя функции в C ++? [Дубликат]

Вы можете попробовать использовать Reflection

Type type = typeof(Employees);
PropertyInfo pi =  this.GetType().GetProperty();
pi.SetField(this, value);

Вот ссылка MSDN: https://msdn.microsoft.com/en-us/library/ms173183.aspx

42
задан ibread 14 January 2010 в 15:46
поделиться

6 ответов

На самом деле существует перегрузка & lt; оператор, который выглядит примерно так:

ostream & operator <<( ostream &, const void * );

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

Изменить: в стандарте C ++ указано:

4.12 Логические преобразования

1 Значение арифметики, перечисления, указателя или указателя на тип члена может быть преобразовано в rvalue типа bool.

Это единственное преобразование, указанное для указателей функций.

39
ответ дан user 18 August 2018 в 20:05
поделиться
  • 1
    +1. Единственным стандартным преобразованием, применимым к указателю на функцию, является (за исключением преобразования lvalue-to-rvalue) преобразование в bool. Примечательно, что вы должны выполнить reinterpret_cast, чтобы преобразовать int (*)() в void *. – avakar 14 January 2010 в 15:40
  • 2
    @avakar Есть ли какой-либо документ, охватывающий правила преобразования указателей на функции? – ibread 14 January 2010 в 15:56
  • 3
    Конечным документом будет стандарт C ++, разделы 4 [conv] и 5.2 [expr.post]. Стандарт не является бесплатным, но вы можете получить последний проект здесь: open-std.org/jtc1/sc22/wg21/docs/papers/2009/n3000.pdf . Помните, что его трудно прочитать, если вы к нему не привыкли. – avakar 14 January 2010 в 16:09
  • 4
    @Neil: void * является единственным преобразованием , но есть еще одна вещь, о которой нужно помнить: манипуляторы - это функции, адреса которых вы передаете в поток. Вместо преобразования их поток вызывает их; если вы передадите указатель на функцию с сигнатурой, подобной манипулятору, поток будет обрабатывать ее как манипулятор и вызывать функцию вместо попытки конвертировать свой адрес вообще. – Jerry Coffin 14 January 2010 в 16:22
  • 5
    @Neil Butterworth @avkar Большое спасибо за вашу помощь. Таким образом, cout будет обрабатывать указатели на функции как bool после того, как выяснит, нет ли другого типа, который можно преобразовать, верно? – ibread 14 January 2010 в 16:27

В C ++ 11 можно было изменить это поведение, указав переменную перегрузку шаблона operator<< (независимо от того, является ли это рекомендуемым или нет, это другая тема):

#include<iostream>
namespace function_display{
template<class Ret, class... Args>
std::ostream& operator <<(std::ostream& os, Ret(*p)(Args...) ){ // star * is optional
    return os << "funptr " << (void*)p;
}
}

// example code:
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}

int main(){
    using namespace function_display;
    // ampersands & are optional
    std::cout << "1. " << &fun_void_void << std::endl; // prints "1. funptr 0x40cb58"
    std::cout << "2. " << &fun_void_double << std::endl; // prints "2. funptr 0x40cb5e"
    std::cout << "3. " << &fun_double_double << std::endl; // prints "3. funptr 0x40cb69"
}
0
ответ дан alfC 18 August 2018 в 20:05
поделиться
  • 1
    Вам также нужна перегрузка, которая будет принимать вариационную функцию C-стиля (с ее конечным эллипсисом) и восемь перегрузок для указателей на функции-члены, каждый из которых может быть неквалифицирован const, volatile или const volatile и может или может не иметь заднего многоточия. – Brian 25 March 2015 в 21:37
  • 2
    @Brian, вы хотите осветить случай указателя на функцию? как double(const *ptr)(double) = &fun_double_double;? (не стесняйтесь редактировать ответ, чтобы уточнить). – alfC 25 March 2015 в 21:44
  • 3
    Вы не можете поместить const в эту позицию. Вы хотите поставить его перед ptr, если вы хотите, чтобы постоянный указатель работал. Во всяком случае, этот случай уже покрыт вашим ответом, но я имею в виду что-то вроде double (T::*ptr)(double) const. – Brian 25 March 2015 в 21:55
  • 4
    @Brian, да, в соответствии с моими испытаниями const (на самом деле const или нет, это та же самая функция, const избыточно). Да, функция указатель-член не была покрыта. Я понял, что это не вопрос. Но я добавил его к ответу сейчас. – alfC 26 March 2015 в 00:50
  • 5
    @Brian, в попытке обобщить решение, но я не мог template<class Ret, class T, class... Args> std::ostream& operator <<(std::ostream& os, Ret(T::* const p)(Args...) const){ // first const ins optional return os << "memfunptr const " << typeid(T).name() <<"::"<< reinterpret_cast<size_t const*>(&p); }, но в основном все различные функции-члены печатают одинаковый номер. – alfC 26 March 2015 в 01:00

Что касается вашего редактирования, вы можете распечатать содержимое чего-либо, обратившись к нему с помощью указателя unsigned char. Пример для указателей на функции-члены:

#include <iostream>
#include <iomanip>

struct foo { virtual void bar(){} };
struct foo2 { };
struct foo3 : foo2, foo { virtual void bar(){} };

int main()
{
    void (foo3::*p)() = &foo::bar;

    unsigned char const * first = reinterpret_cast<unsigned char *>(&p);
    unsigned char const * last = reinterpret_cast<unsigned char *>(&p + 1);

    for (; first != last; ++first)
    {
        std::cout << std::hex << std::setw(2) << std::setfill('0')
            << (int)*first << ' ';
    }
    std::cout << std::endl;
}
9
ответ дан avakar 18 August 2018 в 20:05
поделиться
  • 1
    Большое спасибо! Теперь я знаю, как наблюдать содержимое указателя функции-члена. – ibread 14 January 2010 в 17:18

Указатели каста на (void*) для печати их на cout - это правильная вещь (TM), чтобы делать на C ++, если вы хотите увидеть их значения.

0
ответ дан Eli Bendersky 18 August 2018 в 20:05
поделиться
  • 1
    Это правильная вещь, если не считать, что она вообще не работает. К сожалению, нет никакой гарантии, что указатель на код действительно совместим с указателем на данные. Классический пример (по-настоящему классический, в настоящее время) был в "среде" модель памяти под MS-DOS, где указатель на данные был всего 16 бит, но указатель на код составлял 32 бита. – Jerry Coffin 14 January 2010 в 16:17
  • 2
    @JerryCoffin unsigned long, unsigned long long также может использоваться. – curiousguy 21 October 2011 в 13:42

Вы можете думать, что указатель функции является адресом первой инструкции в машинный код этой функции. Любой указатель можно рассматривать как bool: 0 - false, а все остальное - true. Как вы заметили, при нажатии на void * и заданной в качестве аргумента для оператора вставки потока (<<), адрес печатается. (Показать строго, приведение указателя к функции в void * не определено.)

Без трансляции история немного сложна. Для сопоставления перегруженных функций («разрешение перегрузки») компилятор C ++ собирает набор функций-кандидатов, и из этих кандидатов выбирает «наилучшую жизнеспособную», используя при необходимости неявные преобразования. Морщинка - это правила сопоставления, которые образуют частичный порядок, поэтому множественные наиболее эффективные совпадения вызывают ошибку двусмысленности.

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

  • точное соответствие (, т.е. , не требуется никакого преобразования)
  • promotion (, например , int to float)
  • другие преобразования

Последняя категория включает в себя логические преобразования, и любой тип указателя может быть преобразован в bool: 0 (или NULL ]) - false, а все остальное - true. Последний отображается как 1, когда передается оператору вставки потока.

Вместо этого вместо 0 измените свою инициализацию на

pf = 0;

. Помните, что инициализация указателя с нулевое константное выражение дает нулевой указатель.

4
ответ дан Greg Bacon 18 August 2018 в 20:05
поделиться
  • 1
    Итак, вы имеете в виду, что компилятор стремится обрабатывать любой указатель без известного типа bool? – ibread 14 January 2010 в 15:43
  • 2
    @ibread & quot; известный тип & quot; что такое известный тип? – curiousguy 21 October 2011 в 13:39
  • 3
    @curiousguy Uh ... На самом деле я имею в виду, когда компилятор не может найти точную подпись функции, называемой как приведенные аргументы, скажем, указатель функции в этом конкретном примере, тогда компилятор попытается преобразовать его в bool и, таким образом, сопоставить перегруженной версии с аргументом типа bool. – ibread 25 October 2011 в 19:22
  • 4
    @ibread На самом деле компилятор всегда будет рассматривать все возможные функции кандидатов; если есть функция, использующая правильный тип функции, это будет «лучший», кандидат. Поскольку такой функции нет, единственным кандидатом является функция, принимающая bool. – curiousguy 26 October 2011 в 01:14

Что касается вашего конкретного вопроса,

, как мы можем наблюдать содержимое указателей на функцию-член?

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

4.12 примечание 57:

57) Правило для преобразования указателей на членов (от указателя к члену базы до указателя члену производного) представляется инвертированным по сравнению с правилом для указателей на объекты (от указателя к производному до указателя на базу) (4.10, раздел 10). Эта инверсия необходима для обеспечения безопасности типа. Обратите внимание, что указатель на элемент не является указателем на объект или указателем на функцию, а правила для преобразования таких указателей не применяются к указателям на члены. В частности, указатель на член не может быть преобразован в void *.

Например, вот пример кода:

#include <cstdlib>
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
using namespace std;

class Gizmo
{
public:
    void DoTheThing()
    {
        return;
    };


private:
    int foo_;
};

int main()
{
    void(Gizmo::*fn)(void) = &Gizmo::DoTheThing;

    Gizmo g;
    (g.*fn)();  // once you have the function pointer, you can call the function this way

    bool b = fn;
//  void* v = (void*)fn;    // standard explicitly disallows this conversion
    cout << hex << fn;
    return 0;
}

Отмечу, что мой отладчик ( MSVC9) может рассказать мне фактический физический адрес функции-члена во время выполнения, поэтому я знаю, что должен быть какой-то способ получить этот адрес. Но я уверен, что он несоответствует, не переносится и, вероятно, включает машинный код. Если бы я пошел по этой дороге, я бы начал с адреса указателя функции (например, &fn), отбрасывая это на пустоту * и оттуда. Это также потребует, чтобы вы знали размер указателей (разные на разных платформах).

Но я бы спросил, до тех пор, пока вы можете преобразовать указатель функции-члена в bool и оценить существование указателя, почему в реальном коде вам нужен адрес?

Предположительно, ответ на последний вопрос «так что я могу определить, указывает ли один указатель на функцию на ту же функцию, что и на другой». Справедливо. Вы можете сравнить указатели функций на равенство:

#include <cstdlib>
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
using namespace std;

class Gizmo
{
public:
    void DoTheThing()
    {
        return;
    };

    **void DoTheOtherThing()
    {
        return;
    };**


private:
    int foo_;
};

int main()
{
    void(Gizmo::*fn)(void) = &Gizmo::DoTheThing;

    Gizmo g;
    (g.*fn)();  // once you have the function pointer, you can call the function this way

    bool b = fn;
//  void* v = (void*)fn;    // standard explicitly disallows this conversion
    cout << hex << fn;

    **void(Gizmo::*fnOther)(void) = &Gizmo::DoTheOtherThing;

    bool same = fnOther == fn;
    bool sameIsSame = fn == fn;**

    return 0;
}
0
ответ дан John Dibling 18 August 2018 в 20:05
поделиться
  • 1
    @ Джон Дэйббинг Большое спасибо за ваш ответ. На самом деле, мне просто интересно узнать содержание указателя функции-члена, поэтому я хочу распечатать его, чтобы проверить. :) И есть несколько «**». что заставит компилятор жаловаться. ;) – ibread 15 January 2010 в 15:58
  • 2
    Часть стандарта, который вы цитируете, не имеет ничего общего с ответом. Указатель на член - это совершенно другая вещь, чем указатель на метод (то есть указатель на функцию-член ). – Serge Dundich 28 April 2011 в 09:03
  • 3
    @SergeDundich: ваш комментарий не имеет смысла - указатель на функцию-член является указателем на элемент (другой тип указателя на элемент - указатель на нестатический элемент данных). Все указатели на элементы (как функции, так и данные) формируются одинаково, используя &Type::member, и используются одинаково, используя .* или ->*. – Ben Voigt 14 June 2018 в 15:43
Другие вопросы по тегам:

Похожие вопросы: