{"id":18797380,"url":"https://github.com/asc-community/monobind","last_synced_at":"2025-04-13T16:32:03.732Z","repository":{"id":56525206,"uuid":"295377147","full_name":"asc-community/monobind","owner":"asc-community","description":"C++ binder for mono runtime","archived":false,"fork":false,"pushed_at":"2020-11-02T15:03:01.000Z","size":479,"stargazers_count":20,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T07:48:01.565Z","etag":null,"topics":["cpp","csharp","embedding","mono","scripting"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/asc-community.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-14T10:08:00.000Z","updated_at":"2024-11-15T22:06:24.000Z","dependencies_parsed_at":"2022-08-15T20:20:38.787Z","dependency_job_id":null,"html_url":"https://github.com/asc-community/monobind","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-community%2Fmonobind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-community%2Fmonobind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-community%2Fmonobind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-community%2Fmonobind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/asc-community","download_url":"https://codeload.github.com/asc-community/monobind/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248743886,"owners_count":21154760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","csharp","embedding","mono","scripting"],"created_at":"2024-11-07T22:08:03.293Z","updated_at":"2025-04-13T16:32:03.276Z","avatar_url":"https://github.com/asc-community.png","language":"C++","readme":"# monobind\nmonobind is a lightweight header-only library that exposes C++ types in C# and vice versa, mainly to create C# bindings of existing C++ code. It is inspired by the excellent library [Boost.Python](https://www.boost.org/doc/libs/1_74_0/libs/python) and tries to achieve simular goals of minimizing boilerplate code when implementing interoperability between C++ and C#.\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"monobind-logo.png\"\u003e\n\u003c/p\u003e\n\n## Dependencies\nmonobind requires at least C++14 compatible compiler to run. It only depends on [mono](https://www.mono-project.com/) - cross-platform .NET framework. You do not have to build it - simply install it from the official website to your system.\n\n## Building\nYou can install monobind using Cmake. First of all, you should add the library to your project by executing the following git command: `git submodule add https://github.com/MomoDeve/monobind`. Then simply paste the code below into your `CMakeLists.txt`, replacing names \u0026 paths if necessary:\n```CMake\n// add monobind as subdirectory\nadd_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/submodules/monobind)\n\n// add monobind \u0026 mono include directories\ntarget_include_directories(current_target PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/submodules/monobind/include ${MONO_INCLUDE_DIR})\n// add mono libraries\ntarget_link_libraries(current_target ${MONO_LIBRARIES})\n// add macro definition to do not hard-code path to mono directory\ntarget_compile_definitions(current_target PUBLIC MONOBIND_MONO_ROOT=\"${MONO_ROOT_DIR}\")\n```\n`FindMono.cmake` should find mono installation library. If it fails and you have mono properly installed, I will be glad to see your PR with fixing changes\n\n## Examples\n\nFirstly I should point out that monobind is still in development, so you may find many features missing. I am trying my best to make the library more convinient and waiting for your suggestions (or PRs). Also, if you find the following examples not enough to understand how to use the library, consider looking through some code samples in /examples folder.\n\n### Baseline\nHere is the minimal code you have to write to call C# method from C++ and vice versa. Not arguments are passed between, so it requires the least amount of effort.\n\n*Consider having .cs and .cpp files with the following methods:*\n```cs\nusing System;\nusing System.Runtime.CompilerServices;\n\nnamespace MonoBindExamples\n{\n    public class SimpleFunctionCall\n    {\n        [MethodImplAttribute(MethodImplOptions.InternalCall)]\n        public static extern void HelloFromCpp();\n\n        public static void HelloFromCSharp()\n        {\n            Console.WriteLine(\"C#: Hello!\");\n\n            HelloFromCpp();\n        }\n    }\n}\n```\n```cs\n#include \u003ciostream\u003e\n\nvoid hello_from_cpp()\n{\n    std::cout \u003c\u003c \"C++: Hello!\" \u003c\u003c std::endl;\n}\n```\nIn C# code we create a class with two static methods. One of them is defined in .cs file and calls the other, which is marked with `extern` and should point to our cpp function. To achieve this, we will embed mono runtime into our C++ executable, create a dynamic library from our .cs file and link them together. To begin with, let's initialize mono:\n```cs\n// if you defined this macro in CMake, it should be equal to the path to the mono root directory\nconst char* path_to_mono = MONOBIND_MONO_ROOT;\nmonobind::mono mono(path_to_mono);\nmono.init_jit(\"HelloWorldApplication\");\n```\nWith mono we can now compile our .cs file into a dynamic library and load it into the executable. We can use `monobind::compiler` which accepts path to mono root or mcs compiler. After that assembly can be loaded by passing mono domain and the library name:\n```cs\n    // build csharp library\n    monobind::compiler compiler(mono.get_root_dir());\n    compiler.build_library(\"SimpleFunctionCall.dll\", \"SimpleFunctionCall.cs\");\n\n    // load assembly\n    monobind::assembly assembly(mono.get_domain(), \"SimpleFunctionCall.dll\");\n```\nAnd now we can finally resolve method by passing its cpp implementation as callable object to mono. Invoking method is not that hard too - simply get the method by its signature and call it (you can also pass primitive types, aligned structures, C/C++ strings and arrays between C++ and C# with zero additional code!):\n```cs\n// resolve external method in C# code\nmono.add_internal_call\u003cvoid()\u003e(\"MonoBindExamples.SimpleFunctionCall::HelloFromCpp()\", MONOBIND_CALLABLE(hello_from_cpp));\n\n// call C# method\nmonobind::method method = assembly.get_method(\"MonoBindExamples.SimpleFunctionCall::HelloFromCSharp()\");\nmethod.invoke_static\u003cvoid()\u003e();\n```\n\n### Assemblies, classes, objects\nTo interact with C# classes in most cases you need assemblies. In monobind they are represented by `monobind::assembly` and can be loaded from dynamic library files. If one library depends on another, it will automatically try to load it. Usually you will write something like this:\n```cs\n// get domain from initialized mono runtime object\nmonobind::assembly my_lib(mono.get_domain(), \"my_lib.dll\");\n// or the following, if you do not have reference to mono object:\nmonobind::assembly my_lib(monobind::get_current_domain(), \"my_lib.dll\");\n```\nFrom assembly you can get classes and methods. Classes are represented by `monobind::class_type` and methods are represented by `monobind::method`. To retrieve them, you have to know their signature (namespace and full name for classes, full signature for methods):\n```cs\n// getting C# class with signature Class.Namespace.ClassName \nmonobind::class_type my_class(assembly.get_image(), \"Class.Namespace\", \"ClassName\");\n// getting method handle with signature Class.Namespace.ClassName.YourMethod(int), can be either static or instanced\nmonobind::method m = assembly.get_method(\"Class.Namespace.ClassName::YourMethod(int,single)\");\n```\nAnd with them it is possible to create objects. They are represented by `monobind::object` class and can be easily passed around or accessed:\n```cs\n// if object has no constructor, simply pass domain and its class\nmonobind::object my_obj(mono.get_domain(), my_class);\n// or specify constructor to call (two arguments (int and float) in this case):\nmonobind::object my_obj(mono.get_domain(), my_class, \"::.ctor(int,single)\", 3, 2.5f);\n```\nYou can access fields, call methods or pass them as method arguments:\n```cs\n// call static method with object of some type as first argument and int as second argument:\nmy_method.invoke_static\u003cvoid(monobind::object, int\u003e(my_obj, 3);\n// call instanced method with object as this pointer and int as argument:\nmy_method.invoke_instance\u003cvoid(int)\u003e(my_obj, 3);\n```\n```cs\n// same as above, but my_obj is passed implicitly\nmy_obj.get_method\u003cvoid(int)\u003e(\"::SomeMethod(int)\")(3);\n// and finally, if you need static method but have only object instance:\nmy_obj.get_static_method\u003cvoid(int)\u003e(\"::SomeStaticMethod(int)\")(3);\n```\n```cs\n// you can also access fields\nmonobind::object x = my_obj[\"someField\"];\nmy_obj[\"someField\"] = 3;\n\n// or explicitly cast field to type:\nauto x = my_obj[\"someField\"].as\u003cint\u003e();\n```\n```cs\n// and even properties\nobj.set_property(\"someProp\", 3);\nint prop = obj.get_property(\"someProp\");\n```\n### Getting information about C# types\nThere are a couple utility methods with which you can get reflection information about C# types. They are wrapping mono C-style iterators into C++ ones, which is much more comfortable and support for-range loops:\n```cs\nmonobind::class_type my_class(assembly.get_image(), \"\", \"MyClass\");\nmonobind::object my_obj(monobind::get_current_domain(), my_class);\n\nfor(const auto\u0026 method_obj : my_class.get_methods())\n{\n    std::cout \u003c\u003c method_obj.get_signature() \u003c\u003c std::endl;\n}\n\nfor(const char* field_name : my_obj.get_class().get_fields())\n{\n    std::cout \u003c\u003c field_name \u003c\u003c \" = \" \u003c\u003c my_obj[field_name] \u003c\u003c std::endl;\n}\n\nfor(const char* property_name : my_obj.get_class().get_properties())\n{\n    std::cout \u003c\u003c property_name \u003c\u003c \" = \" \u003c\u003c my_obj.get_property(property_name) \u003c\u003c std::endl;\n}\n```\n\n### Type conversions\nHave you noticed that there is no need to convert types when passing them to mono methods? Because you literally do not have to! All primitive types, structures and arrays and strings are passed by value with automatic conversion between C++ and C# code. It works for method arguments, method return value, fields and callable input arguments in internal call. Here is a list of all built-in conversions in monobind:\n|C++ type      |C# type|C++ type|C# type|C++ type                     |C# type               |\n|--------------|-------|--------|-------|-----------------------------|----------------------|\n|char / uint8_t|byte   |int64_t |long   |std::string / const char*    |string                |\n|int16_t       |int16  |uint64_t|ulong  |std::wstring / const wchar_t*|string                |\n|uint16_t      |uint16 |float   |single |monobind::object             |class_object/any_type |\n|int / int32_t |int    |double  |double |std::vector / std::array     |any_type[]            |\n|uint32_t      |uint   |wchar_t |char   |c-style structure            |struct with std-layout|\n|bool          |bool   |void    |void   |                             |                      |\n\nIf you have types which are not trivially converted between mono and native code, you can also defined your own converters:\n```cs\n// conversion from C++ to C#\ntemplate\u003c\u003e\nstruct monobind::to_mono_converter\u003cyour_type\u003e\n{\n    static MonoObject* convert(MonoDomain* domain, your_type t)\n    {\n        monobind::object obj;\n        // custom code which initializes obj and its fields\n        return obj.get_pointer():\n    }\n};\n```\n```cs\n// conversion from C# to C++\ntemplate\u003c\u003e\nstruct monobind::from_mono_converter\u003cyour_type\u003e\n{\n    static your_type convert(MonoDomain* domain, MonoObject* obj)\n    {\n        your_type t;\n        // custom code which intializes t and its fields\n        return t;\n    }\n};\n```\nIf you need to override behaviour of some primitive type conversions (int, char or your plain struct), you also need to define the following structure. This will help monobind to determine that the type cannot be just reinterpret as memory chunk when passing between runtime and native code:\n```cs\ntemplate\u003c\u003e\nstruct monobind::can_be_trivially_converted\u003cyour_type\u003e\n{\n    static constexpr size_t value = false;\n};\n```\nWith all this utilities, its much easier to call methods and work with their return values. For example, here is the implementation of split function call from C++. Notice how naturally arrays and string are passed to C# methods:\n```cs\nauto split_method = assembly.get_method(\"Utils::SplitString(char[])\");\nauto split_fun = split_method.as_function\u003cstd::vector\u003cstd::string\u003e(const std::string\u0026, std::array\u003cwchar_t, 1\u003e)\u003e();\n\nauto words = split_fun(\"split this line\", { L' ' });\nfor(const std::string\u0026 word : words)\n{\n    std::cout \u003c\u003c word \u003c\u003c std::endl;\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasc-community%2Fmonobind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasc-community%2Fmonobind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasc-community%2Fmonobind/lists"}