In our previous post we learnt about C++/CLI concepts, here is an example how to implement a logger library using C++/CLI which can be used both C# and C++.
Every software product development needs a mechanism to track execution of it application or to report glitches if any. Therefore, a logger becomes an indispensable module of a software package. Moreover, it becomes more rewarding if all logs are saved in coherent and centralized manner irrespective of which language (C# or C++) it is logged from.
C++/CLI based logger therefore turns into a single useful trick to log all important events (not C# events of course) from both C# and C++. Lets see how we can implement one now.
Here is a C++/CLI based logger class which implements from a pure abstract class ILogger which is again C++/CLI based; see the usage of caret added as suffix in ILogger name. Also ref keyword is used in Logger class declaration to make this class a .NET type instead of native C++ class.
Also notice that this class depends on ILogWriter which is pure C# interface and and .NET String and Caller info attributes; these attributes in C# provide a powerful method to associate metadata, or declarative information with code (we can discuss later), here they are used to get source function name (CallerMemberName) and file path (CallerFilePath) from where a particular log is created.
Next find the implementation of Logger class below. It creates the instance of a simple C# data structure class LogData using gcnew keyword. It also uses .NET DateTime struct to fetch time of log creation.
Now our logger class can be instantiated in any C# class to start logging information. Usually a provider class is implemented to provide a singleton logger instance with desired logging destination (e.g. file, event or database logging) across application.
Logger class we have seen above is of course a .NET compatible type can be used in C# directly but it cannot be used in native C++ as it is; native C++ compiler does not recognize special character caret (^) which is used to refer our managed Logger class. We need to create a wrapper written again in C++/CLI using native C++ class declaration. We can call this class as a LoggerProxy; it only delegates a logging call to our original managed Logger class.
Here one more concept is used, Marshalling; it is a process of exchanging data between managed and unmanaged code provided by CLR. One data type in managed context can be marshalled in corresponding data type in unmanaged context or vice versa. So, in above wrapper class, a C++ string is passed to LoggerProxy functions, it is converted into .NET string using marshalling.
msclr::interop::marshal_as<String^>(input);
So, this LoggerProxy class instance can be created in C++ classes to log information. Again, we can use a provider class to get singleton instance of logger with homogeneous logging destination settings. If we use this mechanism, we are ultimately logging information from both C# and C++ software modules using a single logger library in a coherent and centralized manner.
Find complete implementation in my Github repository here.
