How to Convert Complex C# Library to C++
Translating Complex Library
This example demonstrates how to translate five interdependent C# library projects. We’ll use pre-existing projects from ComplexLibrary example located here.
This example consists of five C# projets – BaseLibrary, CommonLibrary, LibraryA, LibraryB and ComplexLibrary. We’ll translate projects one by one starting from the least dependent one.
Translating BaseLibrary
BaseLibrary is a library project consisting of a single .cs source file IBaseInterface.cs
and a project file BaseLibrary.csproj
. This project does not have any special dependencies on other projects or 3rd party assemblies. Also BaseLibrary project directory contains pre-created configuration file BaseLibrary.translator.config
. Let us have a closer look at it.
BaseLibrary.translator.config is quite simple. It begins with an XML declaration, which specifies that the file contains an XML document.
Then goes the XML root element <translator> which is mandatory for Translator configuration XML document
<translator>
Next, the default Translator configuration file is imported using <import> element. The default configuration assigns default values to all configuration options.
<import config="translator.config"/>
And the XML document is finished with closing tag of the root element <translator>:
</translator>
This example assumes that C# BaseLibrary library project should be translated into C++ static library project, which is a default setting.
With C# project at hand and configuration file ready, we can start translating the project.
In order to covert BaseLibrary project we run CMD and navigate to the directory with translator binary:
>cd C:\CodePorting.Translator_Cs2Cpp_19.4\bin\translator
And run Translator:
>CodePorting.Translator.Cs2Cpp.exe -c C:\ComplexNUnitTest\BaseLibrary\BaseLibrary.translator.config C:\ComplexNUnitTest\BaseLibrary\BaseLibrary.csproj C:\output
Translator will print some logs of the translating process to the console window and when it finishes translating, directory C:\output
will contain a directory named BaseLibrary.Cpp
containing the generated C++ source files and Cmake configuration files.
Now we want to use Cmake to generate makefile/project files. Let it be a Visual Studio 2017 x86 project file. In CMD we navigate to the C:\output\BaseLibrary.Cpp
directory
>cd C:\output\BaseLibrary.Cpp
And run Cmake in configuration mode:
>Cmake --G "Visual Studio 15 2017"
And now we can build the sources using either Cmake or Visual Studio. Let us use Cmake:
>Cmake --build . --config Release
The library is built.
Translating CommonLibrary
The second project in this example is located in CommonLibrary
directory. CommonLibrary is a library project consisting of a single .cs source file BaseInterfaceSimplImpl.cs
and a project file CommonLibrary.csproj
. This project has a dependency on BaseLibrary project. This dependency has to be reflected in CommonLibrary project’s configuration file. In our example this configuration file is pre-created, its name is CommonLibrary.translator.config
and it is located in the project’s directory CommonLibrary
. Let us have a closer look at the configuration file.
CommonLibrary.translator.config begins with an XML declaration, which specifies that the file contains an XML document
Then goes the XML root element <translator> which is mandatory for Translator configuration XML document
<translator>
Next, the default Translator configuration file is imported using <import> element. The default configuration will assign default values to all configuration options
<import config="translator.config"/>
Also we need to import a configuration file include_map.config from translated BaseLibrary project that maps public types exported by CommonLibrary library to generated C++ header files in which these types are declared. include_map.config is generated by Translator for each project it translates. Thus, before translating CommonLibrary library project, BaseLibrary project has to be translated first so that Translator generates include_map.config. This is how include_map.config is included in CommonLibrary.translator.config:
<import config="../../output/BaseLibrary.Cpp/include_map.config" />
Here ../../output is a directory that was passed as an output directory to Translator when BaseLibrary project was translated.
This example assumes that C# project CommonLibrary should be translated into C++ shared/dynamic library project, therefore we assign value ‘true’ to make_shared_lib option:
<opt name="make_shared_lib" value="true" export_per_member="true"/>
Next, we want Translator to add some commands to the output CMakeLists.txt file. We do that by adding <cmake_commands> element to the configuration file containing raw Cmake commands
<cmake_commands>
<![CDATA[
The following commands set the output directory for the library’s binary by setting the corresponding properties on the target ${PROJECT_NAME}:
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin")
set_target_properties(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin")
Here ${PROJECT_NAME} is the name of the Cmake project which is equal to the name of the main Cmake executable target.
Because on “DLL-platforms” (i.e. Windows) a DLL shared library is considered by Cmake an executable entity and on “non-DLL-platforms” (i.e. Linux) a shared object is considered by Cmake a library, we set both RUNTIME_OUTPUT_DIRECTORY and LIBRARY_OUTPUT_DIRECTORY properties.
Then the <cmake_commands> element is closed:
]]>
</cmake_commands>
And last but not least, we need to tell Translator that CommonLibrary project depends on BaseLibrary library. We do it using <lib> element:
<lib csname="BaseLibrary">
<cmake_link_template>
<![CDATA[
find_package(BaseLibrary.Cpp REQUIRED CONFIG PATHS "${CMAKE_CURRENT_SOURCE_DIR}/../BaseLibrary.Cpp" NO_DEFAULT_PATH)
target_link_libraries(${PROJECT_NAME}_dependencies INTERFACE BaseLibrary.Cpp)
]]>
</cmake_link_template>
</lib>
Here ${PROJECT_NAME}_dependencies is the name of the Cmake Interface Library target that is defined in the output CMakeLists.txt and is linked to main executable target ${PROJECT_NAME}. Thus libraries linked to ${PROJECT_NAME}_dependencies get automatically linked to ${PROJECT_NAME} target.
Finally the XML document is finished with closing tag of the root element <translator>:
</translator>
With the C# project at hand and configuration file ready, we can convert the project.
In order to covert CommonLibrary project we run CMD and navigate to the directory with translator binary:
>cd C:\CodePorting.Translator_Cs2Cpp_19.4\bin\translator
And run Translator:
>CodePorting.Translator.Cs2Cpp.exe -c C:\ComplexNUnitTest\CommonLibrary\CommonLibrary.translator.config C:\ComplexNUnitTest\CommonLibrary\CommonLibrary.csproj C:\output
Translator will print some logs of the translating process to the console window and when it finishes translating, directory C:\output
will contain a directory named CommonLibrary.Cpp
containing the generated C++ source files and Cmake configuration files.
Now we want to use Cmake to generate makefile/project files. Let it be a Visual Studio 2017 x86 project file. In CMD we navigate to the C:\output\CommonLibrary.Cpp
directory
>cd C:\output\CommonLibrary.Cpp
And run Cmake in configuration mode:
>Cmake --G "Visual Studio 15 2017"
And now we can build the sources using either Cmake or Visual Studio. Let us use Cmake:
>Cmake --build . --config Release
When build finishes, directory D:\output\bin\Release should contain a .dll file CommonLibrary.Cpp.dll.
which has just been built from C++ sources.
Translating LibraryA
The third project in this example is located in LibraryA directory. LibraryA is a library project consisting of a single .cs source file ClassAImpl.cs
and a project file LibraryA.csproj
. This project has a dependency on two previously translated projects BaseLibrary and CommonLibrary. These dependencies have to be reflected in LibraryA project’s configuration file. In our example this configuration file is pre-created, its name is LibraryA.translator.config
and it is located in the project’s directory LibraryA
. Let’s have a closer look at this file.
LibraryA.translator.config begins with an XML declaration, which specifies that the file contains an XML document
Then goes the XML root element <translator> which is mandatory for Translator configuration XML document
<translator>
Next, the default Translator configuration file is imported using <import> element. The default configuration will assign default values to all configuration options
<import config="translator.config"/>
Because LibraryA has dependency on BaseLibrary and CommonLibrary projects, we need to import configuration files include_map.config from both translated projects. This is how include_map.config files are included in LibraryA.translator.config:
<import config="../../output/BaseLibrary.Cpp/include_map.config" />
<import config="../../output/CommonLibrary.Cpp/include_map.config" />
Here ../../output is a directory that was passed as an output directory to Translator when BaseLibrary and CommonLibrary projects were translated.
And last but not least, we need to tell Translator that LibraryA project depends on BaseLibrary and CommonLibrary library. We do it using <lib> element
<lib name="CommonLibrary.Cpp" csname="CommonLibrary">
<cmake_link_template>
<![CDATA[
find_package(CommonLibrary.Cpp REQUIRED CONFIG PATHS "${CMAKE_CURRENT_SOURCE_DIR}/../CommonLibrary.Cpp" NO_DEFAULT_PATH)
target_link_libraries(${PROJECT_NAME}_dependencies INTERFACE CommonLibrary.Cpp)
]]>
</cmake_link_template>
</lib>
<lib csname="BaseLibrary">
<cmake_link_template>
<![CDATA[
find_package(BaseLibrary.Cpp REQUIRED CONFIG PATHS "${CMAKE_CURRENT_SOURCE_DIR}/../BaseLibrary.Cpp" NO_DEFAULT_PATH)
target_link_libraries(${PROJECT_NAME}_dependencies INTERFACE BaseLibrary.Cpp)
]]>
</cmake_link_template>
</lib>
Finally the XML document is finished with closing tag of the root element <translator>:
</translator>
With C# project at hand and configuration file ready, we can convert the project.
In order to covert LibraryA project we run CMD and navigate to the directory with translator binary:
>cd C:\CodePorting.Translator_Cs2Cpp_19.4\bin\translator
And run Translator:
>CodePorting.Translator.Cs2Cpp.exe -c C:\ComplexNUnitTest\LibraryA\LibraryA.translator.config C:\ComplexNUnitTest\LibraryA\LibraryA.csproj C:\output
Translator will print some logs of the translating process to the console window and when it finishes translating, directory C:\output
will contain a directory named LibraryA.Cpp
containing the generated C++ source files and Cmake configuration files.
Now we want to use Cmake to generate makefile/project files. Let it be a Visual Studio 2017 x86 project file. In CMD we navigate to the C:\output\LibraryA.Cpp
directory
>cd C:\output\LibraryA.Cpp
And run Cmake in configuration mode:
>Cmake --G "Visual Studio 15 2017"
And now we can build the sources using either Cmake or Visual Studio. Let us use Cmake:
>Cmake --build . --config Release
The library is built.
Translating LibraryB
The fourth project in this example is located in LibraryB directory. LibraryB is a library project consisting of a single .cs source file ClassBImpl.cs
and a project file LibraryB.csproj
. This project has a dependency on two previously translated projects BaseLibrary and CommonLibrary. These dependencies have to be reflected in LibraryB project’s configuration file. In our example this configuration file is pre-created, its name is LibraryB.translator.config
and it is located in the project’s directory LibraryB
. Let us have a closer look at LibraryB’s configuration file.
LibraryB’s configuration file is much the same as LibraryA’s one described above. One difference is that because this example assumes that LibraryB should be built into a shared dynamic library, this has to be explicitly stated in the configuration file
<opt name="make_shared_lib" value="true" export_per_member="true"/>
And another difference is that we want to set the output directory for the library binary file just like we did it in ComomnLibary’s configuration file described above
<cmake_commands>
<![CDATA[
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin")
set_target_properties(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin")
]]>
</cmake_commands>
With C# project at hand and configuration file ready, we can convert the project.
In order to covert LibraryA project we run CMD and navigate to the directory with translator binary:
>cd C:\CodePorting.Translator_Cs2Cpp_19.4\bin\translator
And run Translator:
>CodePorting.Translator.Cs2Cpp.exe -c C:\ComplexNUnitTest\LibraryB\LibraryB.translator.config C:\ComplexNUnitTest\LibraryB\LibraryB.csproj C:\output
Translator will print some logs of the translating process to the console window and when it finishes translating, directory C:\output
will contain a directory named LibraryB.Cpp
containing the generated C++ source files and Cmake configuration files.
Now we want to use Cmake to generate makefile/project files. Let it be a Visual Studio 2017 x86 project file. In CMD we navigate to the C:\output\LibraryB.Cpp
directory
>cd C:\output\LibraryB.Cpp
And run Cmake in configuration mode:
>Cmake --G "Visual Studio 15 2017"
And now we can build the sources using either Cmake or Visual Studio. Let us use Cmake:
>Cmake --build . --config Release
When build finishes, directory D:\output\bin\Release should contain just built .dll file LibraryB.Cpp.dll
along with previously built CommonLibrary.Cpp.dll
.
Translating ComplexLibrary
The last project in this example is located in ComplexLibrary
directory. ComplexLibrary is a library project that consists of a single .cs source file SomeClass.cs
and a project file ComplexLibrary.csproj
. This project has a dependency on all four previously translated projects – BaseLibrary, CommonLibrary, LibraryA and LibraryB. These dependencies have to be reflected in the ComplexLibrary project’s configuration file. In our example this configuration file is pre-created, its name is ComplexLibrary.translator.config
and it is located in the project’s directory ComplexLibrary
. Let us have a closer look at the configuration file.
ComplexLibrary.translator.config begins with an XML declaration, which specifies that the file contains an XML document
Then goes the XML root element <translator> which is mandatory for Translator configuration XML document
<translator>
Next, the default Translator configuration file is imported using <import> element. The default configuration will assign default values to all configuration options
<import config="translator.config"/>
Also we import include_map.config files from all four previously translated library projects
<import config="../../output/LibraryA.Cpp/include_map.config" />
<import config="../../output/LibraryB.Cpp/include_map.config" />
<import config="../../output/BaseLibrary.Cpp/include_map.config" />
<import config="../../output/CommonLibrary.Cpp/include_map.config" />
This example assumes that C# project ComplexLibrary should be translated into C++ shared/dynamic library project, therefore we assign value ‘true’ to make_shared_lib option:
<opt name="make_shared_lib" value="true" export_per_member="true"/>
Next, we want Translator to add some commands to the output CMakeLists.txt. We do that by adding <cmake_commands> element to the configuration file containing raw Cmake commands
<cmake_commands>
<![CDATA[
The following two commands set the output directory for the library’s binary by setting the corresponding properties on the target ${PROJECT_NAME}:
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin")
set_target_properties(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin")
Then the <cmake_commands> element is closed
]]>
</cmake_commands>
Then, we need to tell Translator that ComplexLibrary project depends on four previously built libraries. We do it using <lib> element:
<lib name="BaseLibrary.Cpp" csname="BaseLibrary">
<cmake_link_template>
<![CDATA[
find_package(BaseLibrary.Cpp REQUIRED CONFIG PATHS "${CMAKE_CURRENT_SOURCE_DIR}/../BaseLibrary.Cpp" NO_DEFAULT_PATH)
target_link_libraries(${PROJECT_NAME}_dependencies INTERFACE BaseLibrary.Cpp)
]]>
</cmake_link_template>
</lib>
<lib name="CommonLibrary.Cpp" csname="CommonLibrary">
<cmake_link_template>
<![CDATA[
find_package(CommonLibrary.Cpp REQUIRED CONFIG PATHS "${CMAKE_CURRENT_SOURCE_DIR}/../CommonLibrary.Cpp" NO_DEFAULT_PATH)
target_link_libraries(${PROJECT_NAME}_dependencies INTERFACE CommonLibrary.Cpp)
]]>
</cmake_link_template>
</lib>
<lib name="LibraryA.Cpp" csname="LibraryA">
<cmake_link_template>
<![CDATA[
find_package(LibraryA.Cpp REQUIRED CONFIG PATHS "${CMAKE_CURRENT_SOURCE_DIR}/../LibraryA.Cpp" NO_DEFAULT_PATH)
target_link_libraries(${PROJECT_NAME}_dependencies INTERFACE LibraryA.Cpp)
]]>
</cmake_link_template>
</lib>
<lib name="LibraryB.Cpp" csname="LibraryB">
<cmake_link_template>
<![CDATA[
find_package(LibraryB.Cpp REQUIRED CONFIG PATHS "${CMAKE_CURRENT_SOURCE_DIR}/../LibraryB.Cpp" NO_DEFAULT_PATH)
target_link_libraries(${PROJECT_NAME}_dependencies INTERFACE LibraryB.Cpp)
]]>
</cmake_link_template>
</lib>
Here ${PROJECT_NAME}_dependencies is the name of the Cmake Interface Library target that is defined in the output CMakeLists.txt file and is linked to main executable target ${PROJECT_NAME}. Thus libraries linked to ${PROJECT_NAME}_dependencies get automatically linked to ${PROJECT_NAME} target.
Finally the XML document is finished with closing tag of the root element <translator>
</translator>
With the C# project at hand and configuration file ready, we can convert the project.
In order to covert ComplexConsoleApp project we run CMD and navigate to the directory with translator binary:
>cd C:\CodePorting.Translator_Cs2Cpp_19.4\bin\translator
And run Translator:
>CodePorting.Translator.Cs2Cpp.exe -c C:\ComplexLibrary\ComplexLibrary\ComplexLibrary.translator.config C:\ComplexLibrary\ComplexLibrary\ComplexLibrary.csproj C:\output
Translator will print some logs of the translating process to the console window and when it finishes translating, directory C:\output
will contain a directory named ComplexLibrary.Cpp
containing the generated C++ source files and Cmake configuration files.
Now we want to use Cmake to generate makefile/project files. Let it be a Visual Studio 2017 x86 project file. In CMD we navigate to the C:\output\ComplexLibrary.Cpp
directory
>cd C:\output\ComplexLibrary.Cpp
And run Cmake in configuration mode:
>Cmake --G "Visual Studio 15 2017"
And now we can build the sources using either Cmake or Visual Studio. Let us use Cmake:
>Cmake --build . --config Release
When build finishes, directory D:\output\bin\Release should contain four files: CommonLibrary.dll, LibraryB.dll, ComplexLibrary.Cpp.dll, which has just been built from sources, and aspose_cpp_vc140.dll, which was copied from Translator installation directory during a post-build step.