C++/CLI, an Alpha in the disguise of a 4th release.
I must say it took me a while to choose a title for this post; I was pondering whether "C++/CLI Sux" would express my feelings better.
It all began half a year ago, when I got the responsibility of a new algorithmic infrastructure project for our systems, which by definition must be compliant with ANSI-C++ for HPUX/Linux and other native systems, Win32/Visual-C++ for abundant performance systems and of course CLS compliancy for user applications which are built over the .NET environment.
The architecture I've formulated was based on an ANSI solution with environmental compiler directives (e.g. extern for the legacy, and __declspec for the VC compiler). I was planning to provide an answer to the CLS compliancy demand with a proxy-like wrapper solution.
Choosing between static DLLImports and OO-trustworthy C++/CLI
Basically, I had two options for accessing my unmanaged environment from a C# application (That's after disqualifying any loosely-coupling solution due to certain deployment requirements).
The first option was to provide static DLLImport based solution, which would staticly wrap all of my DLL entry points, and would flat my models; Also, choosing this wrapping technique would require massive marshaling treatment and would deprive us of software engineering decisions.
The second option, which was my favorite, is to provide a wrapping solution based on the C++/CLI environment. By taking advantage of the unsafe calls which are prohibited in any other case, with the mscorlib members and the strength of the managed sandbox - I could maintain my engineering decisions, and provide a fully funcation Object-Oriented solution with a robustic model which can be scalled up to support future growth.
Oh boy.. How wrong was I...
So no wonder why I've decided to choose C++/CLI as my intermediate layer between pure .NET logics and no-so-pure unmanaged algorithms.
In an early stage, I thought I would supply a set of compiler directives for prefixing a class (e.g. 'public ref class' instead of 'class'), lists (IList or ICollection generics instead of STL's vector, queue, etc. templates) and I would compile with the /clr:oldSyntax flag. However, the warning which claims that oldSyntax is obsolete, and the fact I need to supply a package with support for 20 years, made me to give up on that one. Using the /clr with its new syntax would make me to provide more directives for pointers & references (managed ^ and % instead of unmanaged & and *) and other ugly stuff.
After a short trial and error, I've eliminated the directives option and decided to build a wrapper project for the dynamic and static libraries. Basically, the code became too ugly, and code-aware tools such as DocumentX and Enterprise Architect could not understand the directives and failed to explore my source.
I've started by modeling a simple Proxy-Pattern (with some constraints due to the lack of interfaces in some of the environments I had to support) solution.
All the public methods were reflected in an outer managed envelope, calling a private unmanaged member field (Similar to the Adapter-Pattern); And the tree was happy.
The next stage was to create a simple case study Mxxxxxx (Managedxxxxxx) project, and wrap one of the common entities in my model; And the tree was, yet, happy.
Now, after making this proof of concept, I've started implementing the hierarchical object graph for my project. Base classes with protected fields, public specific methods with the relevant castings, assimilation of .NET types (e.g. DateTime, TimeSpan instead of the unmanaged alternatives) in the signatures of the public methods; And the tree was happy, but then he pressed Ctrl+Shift+B...
Linker error LNK2022
This linker error is my favorite. Not only because Microsoft's fix to this was "To resolve this problem, contact Microsoft Product Support Services to obtain the hotfix" but because I was suffering of it twice, and I still cannot exactly tell how I got rid of it.
In my first encounter with LNK2022 error I do not know what exactly crossed my mind. I thought I could override a pure virtual member which was declared in an abstract class from within my derived class.
After dealing almost a day with this linker error and doing some irrational things, I've managed to get over it by ignoring the 'using namespace XXXXXXX' declarations at the top, and by adding 'XXXXXXX::MyClass::...' before the return types of methods which returns instances which are not sitting on the managed heap. You say voodoo? I say Baaaah.
My second encounter with the LNK2022 error was quite ambiguous... I've done nothing. The solution compiled and linked successfully.. I've rebooted my machine, and Kazam! LNK2022 error - "Inconsistent method declarations in duplicated types". For some reason, my constructor (or by its pet name - my .ctor) was misbehaving.. I've googled for an hour, and then came across this response by JulianJoseph:
Hi,
Well a simple clean solution did the trick for me. Thank you Microsoft...
---------------------
Julian
And viola! Thank you Julian.. It works for me too!
Compiler Errors C2248 and C3767
For an unknown (at the time) reason, I was getting C2248 error over and over again.. I saw that I'm getting this error even when I was calling a public method of the base class. After doing my magic, the error was replaced by a new one which was by 1519 better than before, error C3767 was the first clue of the problem.
Well.. this one is my fault.. I did not pay enough attention to the list of breaking changes in the managed compiler. It seems that by default, when compiling with the /clr flag - all the unmanaged classes are being treated as private unless applying the public access modifier. Here I had to add a define which looks like this:
#ifdef _MANAGED
#define PUBLIC public
#else
#define PUBLIC
#endif
Next, I of course had to change all of my class declarations to support the new syntax.
Random errors - with no reference number
I'm not going to talk about all the other identified (or so) errors I've had to deal with. I've only mentioned these three because I've had hard time solving it. I had dozens of errors to deal with, both in the linker and the compiler.
I do want to mention the system-ghosts. All of those voodoo errors which had no numbers.. I had to deal with at least ten sudden-linker-death cases, which I could not reproduce.
That's of course without mentioning the sudden death of the IDE itself in some cases... But I can always blame the Clear-Case client for those.
For conclusion
After almost two weeks - My wrapper classes are working; Great even. The project is functioning, and I can finally pack my stuff and fly to LA to attend the upcoming PDC. However, I must warn you all - think, and rethink twice before using C++/CLI in your projects. I would refer to it as an Alpha or an early stage Beta of a concept demonstration. Think carefully before using it for an operational project with deadlines and human beings which need to later on maintain the code.
I hate C++/CLI. I really do.
blog comments powered by