__clrcall
Блок, относящийся только к системам Microsoft
Указывает, что функцию можно вызвать только из управляемого кода. Используйте __clrcall для всех виртуальных функций, которые будут вызываться только из управляемого кода. Однако это соглашение о вызовах невозможно использовать для функций, которые будут вызываться из машинного кода.
С помощью __clrcall можно повысить производительность при вызове из управляемой функции в виртуальную управляемую функцию или из управляемой функции в управляемую функцию с помощью указателя.
Точки входа представляют собой отдельные функции, создаваемые компилятором. Если функция имеет машинные и управляемые точки входа, одна из них будет фактической функцией с реализацией функции. Другая функция будет отдельной функцией (преобразователем), которая вызывает фактическую функцию и позволяет среде CLR выполнять PInvoke. Если функция отмечена как __clrcall, это значит, что реализация функции должна быть MSIL и что функция машинной точки входа не создается.
При получении адреса собственной функции, если функция __clrcall не определена, компилятор использует машинную точку входа. __clrcall указывает, что функция является управляемой и нет необходимости выполнять преобразование из управляемой функции в собственную. В этом случае компилятор использует управляемую точку входа.
Если используется /clr (не /clr:pure или /clr:safe) и не используется __clrcall, при получении адреса функции всегда возвращается адрес функции машинной точки входа. При использовании __clrcall функция машинной точки входа не создается, поэтому получается адрес управляемой функции, а не функция преобразователя точки входа. Дополнительные сведения см. в разделе Двойное преобразование (С++).
/clr (компиляция CLR) подразумевает, что все функции и указатели функций являются __clrcall и компилятор не позволит отметить функцию в единице компиляции иначе, чем __clrcall. Если используется /clr:pure, __clrcall можно определить только в указателях функций и внешних объявлениях.
Можно явно вызвать функции __clrcall из существующего кода C++, который был скомпилирован с помощью /clr, пока функция имеет реализацию MSIL. Функции __clrcall невозможно вызвать непосредственно из функций, которые имеют встроенный код на языке ассемблера и вызывают зависящие от ЦП встроенные функции, даже если эти функции скомпилированы с помощью /clr.
Указатели функций __clrcall предназначены для использования только в домене приложения, в котором они были созданы. Вместо передачи указателей функций __clrcall между доменами приложения используйте CrossAppDomainDelegate. Дополнительные сведения см. в разделе Домены приложений и Visual C++.
Пример
// clrcall.cpp
// compile with: /clr:oldSyntax /LD
void __clrcall Test1( ) {}
void (__clrcall *fpTest1)( ) = &Test1;
Обратите внимание, что если функция объявляется с помощью __clrcall, код создается при необходимости, например, при вызове функции.
// clrcall2.cpp
// compile with: /clr
using namespace System;
int __clrcall Func1() {
Console::WriteLine("in Func1");
return 0;
}
// Func1 hasn't been used at this point (code has not been generated),
// so runtime returns the adddress of a stub to the function
int (__clrcall *pf)() = &Func1;
// code calls the function, code generated at difference address
int i = pf(); // comment this line and comparison will pass
int main() {
if (&Func1 == pf)
Console::WriteLine("&Func1 == pf, comparison succeeds");
else
Console::WriteLine("&Func1 != pf, comparison fails");
// even though comparison fails, stub and function call are correct
pf();
Func1();
}
В следующем примере показано, что можно определить указатель функции, например объявить, что указатель функции будет вызываться только из управляемого кода. Это позволит компилятору непосредственно вызвать управляемую функцию и избежать машинной точки входа (проблема двойного преобразования).
// clrcall3.cpp
// compile with: /clr
void Test() {
System::Console::WriteLine("in Test");
}
int main() {
void (*pTest)() = &Test;
(*pTest)();
void (__clrcall *pTest2)() = &Test;
(*pTest2)();
}