问题
I am currently working on a visual editor to build Finite State Machines. The core is c++ since the built FSM will run in a game. The editor is c#. I was able to get a CLI wrapper going so I can build everything I need in the c# side. The last thing I am trying to do, is to be able to expose a templated class to c#.
I started by creating a managed class:
template <typename T>
public ref class TestTemp
{
private:
ClassToWrap<T>* m_val;
public:
TestTemp(T val) :
{
m_val = new ClassToWrap<T>();
}
}
Then since templates are generated at compile time, I am forcing the generation of the type with template specialization.
template ref class FSMWrapper::TestTemp<float>;
I tried to to the specialization in several places, cpp, header, also in main just in case, I even tried specific instantiation in main like this:
FSMWrapper::TestTemp<float> t(10.0f);
I even tried to explicitely tell to export the symbol like I would do in regular c++ but compiler complains that I cannot do that with a managed type.
After all this I did not managed to get the symbol appear in the c# namespace, (everything else appears, so yeah, the wrapper works as expected).
Also If I remove the template in the wrapper and just call it TestTempFloat and force internally an instantiation of float it works.
public ref class TestTempFloat
{
private:
ClassToWrap<float> m_val;
public:
TestTempFloat(float val) :
{
m_val = new ClassToWrap<float>();
}
};
What I am trying to do, is it even possible? By googling around looks like it is, but people just say, wrap it in a CLI type and force the symbol generation. If is possible what am I doing wrong?
If is not possible I will just have to do the specialized wraps manually, not pretty but I know it works.
I also tried to wrap it in a generic instead of a template, but then I cannot feed the T generic type as a template type.
PS: I know there is not a destructor to free the memory, this is just a dummy test to keep example short.
回答1:
As you have seen, managed templates aren't accessible outside of the assembly where they are located. Basically the idea is to expose the managed template as a generic interface which can be sent across assembly boundaries.
So in your case you would want to create a ITestTemp
, something like this...
generic<typename T> public interface class ITestTemp
{
public:
TestTemp(T val);
}
That is the interface that you will export across assemblies. Now you will have to convert your managed template into that generic interface to do so you can use inheritance, something with the following signature (internals omitted for simplicity)
templace<typename T> ref class TestTemp : ITestTemp<T>
Once you have that now you will have to do the "compiler's work" (what is normally just automagically handled for a regular C++ template) for it to convert between the two so to speak. So you will have to create a factory method that will create the specific instances that you are looking for. It would look like this
public ref class TestTempFactory
{
public:
generic<typename T> static ITestTemp<T>^ Create()
{
if (T::typeid == String::typeid)
{ return (ITestTemp<T>^) gcnew TestTemp<String>(); }
//more cases as needed...
}
}
I hope that explains it well enough, if not let me know.
回答2:
If you implement a full-fledged subclass of your template class, that should do the trick. You will need to implement all the constructors, but just as a pass-through to the base class's constructors; no actual code.
public ref class TestTempFloat : TestTemp<float>
{
TestTempFloat(float val) : TestTemp(val) { };
};
If you have a lot of these, you could make use of the preprocessor:
#define IMPLEMENT_TESTTEMP(namesuffix, type) \
public ref class TestTemp ## namesuffix : TestTemp<type> \
{ \
TestTemp ## namesuffix(type val) : TestTemp(val) { }; \
};
IMPLEMENT_TESTTEMP(Float, float)
IMPLEMENT_TESTTEMP(Double, double)
IMPLEMENT_TESTTEMP(Int, int)
来源:https://stackoverflow.com/questions/35614760/expose-c-cli-templated-wrapper-to-c-sharp