Есть ли способ вызвать функцию-член без оператора. * Или — & gt; *

Ниже метод вызова D::foo Функция с помощью указателя на член генерирует ошибку: должен использовать .* или же ->* вызвать функцию указателя на член в ‘f (…)’
.. конечно, это не то, как мы вызываем функции указателя на член.

Правильный способ звонка (d.*f)(5); ИЛИ ЖЕ (p->*f)(5);

Мой вопрос: есть ли способ вызвать функцию-член класса без объекта класса с левой стороны? Интересно, могли бы мы передать объект класса (this) как обычный аргумент?

На мой взгляд, в конце дня (на уровне сборки / двоичного кода) все функции-члены класса являются нормальными функциями, которые должны работать с n + 1 аргументами, где (+1 для this)

Если мы говорим о D::foo функция ниже, на уровне сборки / двоичного кода она должна оперировать двумя аргументами:

  1. Сам объект класса (указатель на объект класса D называется this)
  2. и int,

Итак, есть ли способ (или взломать), чтобы позвонить D::foo с объектом класса, переданным ему в качестве аргумента функции вместо использования . or -> or .* or ->* операторы на объекте класса?

Образец кода:

#include <iostream>
using namespace std;

class D {
public:
void foo ( int a ) {
cout << "D" << endl;
}

int data;
};//typedef void  __cdecl ( D::*  Func)(int);
typedef void ( D::*  Func)(int);

int main ( void )
{
D d;

Func f = &D::foo;
f(&d, 5);

return 1;
}

Один метод использует Bind Bind, т.е.

(boost:: bind (&D::foo, &d, 5)) ();

РЕДАКТИРОВАТЬ:
«Обратите внимание, я не ищу версию этой программы, которая работает, я знаю, как заставить ее работать»

10

Решение

Вы действительно хотите вызвать функцию-член без использования . или же ->? В самом деле, действительно? Ну ладно…

Evil.h:

#ifdef __cplusplus
extern "C" {
#endif

struct MyStruct
{
#ifdef __cplusplus
MyStruct();

void method(int);
#endif
};

#ifdef __cplusplus
}
#endif

Evil.cc:

#include <iostream>

#include "evil.h"
MyStruct::MyStruct() { std::cout << "This is MyStruct's constructor" << std::endl; }

void MyStruct::method(int i) { std::cout << "You passed " << i << std::endl; }

Evil.c:

#include "evil.h"
int main()
{
struct MyStruct my_struct;
_ZN8MyStructC1Ev(&my_struct); /* MyStruct::MyStruct() */

_ZN8MyStruct6methodEi(&my_struct, 3); /* MyStruct::method(int) */

return 0;
}

Это работает для моей комбинации gcc и g ++ в Linux, но само собой разумеется, что она опирается на платформу ABI и нарушает стандарт C89 при вызове функций вида символ заглавной буквы. Это почти наверняка не будет работать с виртуальными функциями, и я не склонен пытаться. Это также может быть самой злой вещью, которую я когда-либо писал. Но до сих пор…


РЕДАКТИРОВАТЬ: Цитировать ОП:

На мой взгляд, в конце дня (на уровне сборки / двоичного кода) все функции-члены класса являются обычными функциями, которые должны работать с n + 1 аргументами, где (+1 для this)

Хотя это верно, что каждый компилятор, начиная с CFront, делал это таким образом, это всего лишь деталь реализации. Стандарт C ++ находится в трудном положении не указать, как должны быть реализованы функции-члены, как они должны себя вести.

Поскольку это детали реализации, разные платформы делают это по-разному. Это выходит за рамки только названия искажения. Например, соглашение о вызовах, используемое в Linux, указывает, что this передается в качестве первого аргумента; другие реализации (Borland, IIRC?) проходят this как прошлой аргумент.

Итак, если вы хотите рассматривать функции-члены как обычные функции с дополнительным this, тогда вы должны ограничить себя определенным ABI. Этот пост служит примером того, как вы можете это сделать (или, скорее, примером того, почему вы действительно не должны этого делать!)

Итак, есть ли способ (или взлом) для вызова D :: foo с объектом класса, переданным ему в качестве аргумента функции вместо использования. или -> или. * или -> * операторы на объекте класса?

Отвратительный грязный хак для конкретной платформы …

19

Другие решения

Я не совсем уверен, что вы спрашиваете. Не существует «искусственного ограничения» на вызов функций через указатели на члены; вам просто нужно использовать правильный синтаксис:

(d.*f)(5);  // for objects or references
(p->*f)(5); // for pointers

bind не делает никакой «магии»; где-то в своей реализации, он делает именно это.

в конце дня это функция, которая принимает два аргумента

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

14

Единственный способ, который я вижу, — заменить указатель на функцию typedef-ed классом, который ведет себя как функция.

Вы ищете что-то подобное?

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

#include <iostream>
using namespace std;

class D {
public:
void foo ( int a ) {
cout << "D" << endl;
}

int data;
};

template<class Object, class Param, class Function>
class FunctionPointerHelper
{
private:
Function m_function;
public:
FunctionPointerHelper(Function function) :
m_function(function)
{
}
void operator=(Function function)
{
m_function = function;
}
void operator()(Object& object, Param param) const
{
(object.*m_function)(param);
}
};

#define TYPEDEF_NICE_FUNCTION(RESULT, CLASS, NEW_TYPENAME, PARAM) \
typedef RESULT ( CLASS::* NEW_TYPENAME_INTERMEDIATE)(PARAM) ; \
typedef FunctionPointerHelper<CLASS, PARAM, NEW_TYPENAME_INTERMEDIATE> NEW_TYPENAME;

TYPEDEF_NICE_FUNCTION(void, D, Func, int)

int main ( void )
{
D d;

Func f = &D::foo;
f(d, 5);

return 1;
}
4

Есть ли способ вызвать функцию-член класса без объекта класса с левой стороны?

Два способа без сборки:

#include <iostream>
using namespace std;

class D {
public:
void foo ( int a ) {
cout << "D" << endl;
}

static void foo(D& d, int a)
{
d.foo(a);
}

int data;
};

void foo(D& d, int a)
{
d.foo(a);
}

int main ( void )
{
D d;

D::foo(d, 5);
foo(d, 5);

return 0;
}
3

но должен быть способ избежать этого искусственного ограничения
навязанный с ++

Что вы подразумеваете под «искусственное ограничение»? Это просто синтаксис, как его определяет язык. Что с этим не так? «Магия» bind() будет использовать ->* Оператор внутри, чтобы вызвать функцию.

2

Да, в стандарте C ++ 11 есть абстракция, называемая ВЫЗОВ который относится к вызываемым объектам. Объекты указателя на функцию-члена (PTMF) могут вызываться и this передается как первый (обычный) аргумент.

Здесь нет std::invoke функция, хотя это было предложено. Чтобы получить функтор от любого объекта Callable, вы можете использовать std::bind, который является производным от Boost.bind.

Это отлично работает:

int main ( void )
{
D d;

auto f = std::bind( &D::foo, _1, _2 );
f(&d, 5);
}

http://ideone.com/VPWV5U

2

Да, это правда, что с достаточным количеством искажений вы можете избежать синтаксиса «новой» функции-члена, но я должен спросить: с чего бы вы? Синтаксис способствует пониманию того, что функция-член каким-то образом вызывается из окружающего объекта. Учитывая существование виртуальных функций, это действительно (и должно быть) так. Он также автоматизирует указатель this, который требует вызова виртуальных функций (и поддержки виртуального наследования).

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

Это в конечном итоге выглядит так:

fastdelegate::FastDelegate0<> functionObject;
SomeClass someInstance;

//fill in object and function information in function object
functionObject.bind(&someInstance,&SomeClass::someFunction);

//call function via object.  Equivalent to: someInstance->someFunction();
functionObject();

Это очень похоже на то, что делают boost :: bind и друзья, но в целом быстрее (хотя, очевидно, менее переносимо).

Под шаблонами и перегрузкой операторов, используемых здесь, есть некоторая математика (внутри функции связывания), которая выясняет, как изменить &someInstance в указатель this, необходимый для SomeClass :: someInstance. Он также находит фактический адрес базовой функции и записывает оба значения для последующего использования. Когда вызов происходит, он заставляет компилятор «делать правильные вещи» с помощью некоторой хитрости, используя ->*, Но, если вы действительно хотите избежать даже полагаться на ->* Оператор, который вы могли бы, в этот момент, сделать несколько типов приведений и превратить указатель в указатель функции «__thiscall» (хорошо, если вы находитесь в Windows):

Соглашение о вызовах __thiscall используется для функций-членов и
соглашение о вызовах по умолчанию, используемое функциями-членами C ++, которые делают
не использовать переменные аргументы. При __thiscall вызываемый абонент очищает
стек, что невозможно для функций vararg. Аргументы выдвигаются
в стеке справа налево, с указателем this
через регистр ECX, а не в стеке, на архитектуре x86.

Итак, что именно вам не понравилось: someInstance-> someFunction ()?

1

Стандарт необычайно ясен и совершенно однозначен в этом вопросе. Первое предложение раздела 5.2.2 (C ++ 11) гласит:

Существует два вида вызова функций: обычный вызов функции и вызов функции-члена.

И это все — это разные вещи, и вы не можете их перепутать.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector