编译器错误 C2158 的最佳解决方法:make_public 不支持本机模板类型
我有两个 c++/cli dll(即用 /clr 编译),其中 A.dll 引用 B.dll。在程序集 B 中,我有一个方法 GetMgdClassB,我想从程序集 A 调用。这是程序集 B (B.cpp) 中的代码:
namespace B
{
public class NativeClassB
{
public:
NativeClassB();
// ...
};
public ref class MgdClassB
{
public:
static MgdClassB ^ GetMgdClassB(const std::vector<NativeClassB *> & vecNativeBs)
{
// ...
vecNativeBs;
return gcnew MgdClassB();
}
};
}
请注意,方法 GetMgdClassB 采用 std::vector。在程序集 A 中,我尝试使用以下代码 (A.cpp) 调用此方法:
namespace B
{
class NativeClassB;
}
#pragma make_public(std::vector<B::NativeClassB *>)
namespace A
{
void Foo()
{
std::vector<B::NativeClassB *> vecNativeBs;
B::MgdClassB::GetMgdClassB(vecNativeBs);
}
}
当我编译 A.cpp 时,出现以下错误:
error C2158: 'std::vector<_Ty>' : #pragma make_public directive is currently supported for native non-template types only
我想添加此编译指示的原因是因为本机类型对于程序集是私有的默认情况下。如果删除该编译指示,则会出现以下错误(如预期):
error C3767: 'B::MgdClassB::GetMgdClassB': candidate function(s) not accessible
因为模板实例化类型 std::vector
是程序集私有的。
尝试的解决方案
1. 使用 void *,破坏类型安全:
更改方法 GetMgdClassB
以获取 void *
并传递 GetMgdClassB
的地址code>std::vectorGetMgdClassB
中。然后我可以static_cast
将void *
传入std::vector
2. 为 NativeClassB 创建托管包装器,传递托管容器
创建一个托管类,例如 ref class NativeClassBWrapper
,其唯一目的是保留对本机 NativeClassB 的引用。更改 GetMgdClassB 以获取 NativeClassBWrappers 的托管容器(例如 List
)。这样做的缺点是必须在调用 GetMgdClassB 之前创建并填充一个新的托管容器,然后在托管类 B 中,我必须将其重新打包到本机容器中 std::vector
目前,我倾向于使用解决方案 #1,因为 (a) 它不会引入任何性能问题,并且 (b) 我只会在在某些情况下,我不喜欢失去类型安全性,但考虑到当前编译器使本机模板实例化类型可见的能力不足,这似乎是合理的。
<
相关
问题:
a href=" https://stackoverflow.com/questions/947213/c-cli-candidate-functions-not-accessible">C++ CLI 错误 C3767:候选函数无法访问
I have two c++/cli dlls (i.e. compiled with /clr) where A.dll references B.dll. In assembly B, I have a method, GetMgdClassB, I'd like to call from assembly A. Here is the code in assembly B (B.cpp):
namespace B
{
public class NativeClassB
{
public:
NativeClassB();
// ...
};
public ref class MgdClassB
{
public:
static MgdClassB ^ GetMgdClassB(const std::vector<NativeClassB *> & vecNativeBs)
{
// ...
vecNativeBs;
return gcnew MgdClassB();
}
};
}
Notice that the method GetMgdClassB takes a std::vector. In assembly A, I attempt to call this method with the following code (A.cpp):
namespace B
{
class NativeClassB;
}
#pragma make_public(std::vector<B::NativeClassB *>)
namespace A
{
void Foo()
{
std::vector<B::NativeClassB *> vecNativeBs;
B::MgdClassB::GetMgdClassB(vecNativeBs);
}
}
When I compile A.cpp, I get the following error:
error C2158: 'std::vector<_Ty>' : #pragma make_public directive is currently supported for native non-template types only
the reason I wanted to add this pragma is because native types are private to the assembly by default. If I remove the pragma I get the following error (as expected):
error C3767: 'B::MgdClassB::GetMgdClassB': candidate function(s) not accessible
since the template instantiation type std::vector<B::NativeClassB *>
is private to the assembly.
Attempted Solutions
1. Use void *, break type safety:
Change the method, GetMgdClassB
to take a void *
and pass the address of the std::vector<NativeClassB *>
to the method. In GetMgdClassB
. I can then static_cast
the passed in void *
to std::vector<NativeClassB *> *
. This, of course, works, but breaks type safety.
2. Create a Managed wrapper for NativeClassB, pass a managed container
Create a managed class, say ref class NativeClassBWrapper
who's sole purpose is to hang on to a reference to the native NativeClassB. Change GetMgdClassB to take a managed container of NativeClassBWrappers (e.g. List<NativeClassBWrapper ^> ^
). This has the downside of having to create and populate a new managed container prior to calling GetMgdClassB, and then within managed class B, I have to repackage it into the the native container std::vector<NativeClassB *>
(since the code in B deals with this type.
Currently, I'm leaning toward going with Solution #1, since (a) it doesn't introduce any performance concerns and (b) I'll only be doing this in a few cases. I don't like losing the type safety, but it seems justifiable given the current deficiency in the compiler's ability to make native template instantiation types visible.
Question:
Are there better work arounds?
Related Question:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我不知道有什么方法可以导出该类型。如果您必须拥有该函数签名,我会倾向于混合使用托管和本机导出(使用本机类型的托管函数无论如何都不能被其他语言使用),并且可能在调用本机时使用延迟加载导出,以便您有机会捕获以通常的 .NET 方式查找程序集的错误。
但是您的特定函数可能会出现问题,因为它在签名中同时使用托管类型和复杂的本机类型。
一般来说,最佳实践是根本不跨 DLL 边界传递本机 C++ 类,因为这会导致违反“单一定义规则”。
对于这种特殊情况,我的建议是制作一个实现 ICollection 的包装器。就像您的解决方案#2 一样,这可以解决问题,而无需实际将所有元素复制到新的数据结构中。
I'm not aware of any way to export that type. If you have to have that function signature, I would lean in the direction of using a mix of managed and native exports (managed functions using native types can't be consumed by other languages anyway), and maybe use delay loading when calling the native exports so you have a chance to trap errors finding the assembly in the usual .NET way.
But your particular function may be problematic since it uses both managed types and complex native types in the signature.
In general, the best practice is to not pass native C++ classes across DLL boundaries at all, since this sets you up for One Definition Rule violations.
For this particular situation, my suggestion is to make an wrapper that implements
ICollection
. That cures the problem just like your solution #2, without ever having to actually copy all the elements into a new data structure.我在另一个论坛上收到了 Mike Danes 的解决方案:
http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/b43cca63-b0bf-451e-b8fe-74e9c618b8c4/
基本上,解决方案是创建一个本机程序集 B 中的包装器(称为 VectorOfNativeB)保存对 std::vector 的指针或引用。导出 VectorOfNativeB 并使其公开可见。更改方法 GetMgdClassB 以获取指针或引用 VectorOfNativeB。
[将其发布在这里以供将来参考并查看这里是否有人对此解决方案有任何评论]。
I received a solution from Mike Danes on another forum:
http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/b43cca63-b0bf-451e-b8fe-74e9c618b8c4/
Basically, the solution is to create a native wrapper (call it VectorOfNativeB) in assembly B that holds on to a pointer or reference to the std::vector. Export VectorOfNativeB and make it publicly visible. Change method GetMgdClassB to take a pointer or reference VectorOfNativeB.
[posted this here for future reference and to see if anyone here has any comments about this solution].