https://github.com/n0bra1n3r/cinterop
A C/C++ interop library for the Nim programming language
https://github.com/n0bra1n3r/cinterop
c cplusplus cpp interop nim nim-lang
Last synced: about 2 months ago
JSON representation
A C/C++ interop library for the Nim programming language
- Host: GitHub
- URL: https://github.com/n0bra1n3r/cinterop
- Owner: n0bra1n3r
- Created: 2021-09-22T10:53:17.000Z (over 3 years ago)
- Default Branch: staging
- Last Pushed: 2023-02-24T23:41:02.000Z (over 2 years ago)
- Last Synced: 2025-03-23T18:47:45.203Z (2 months ago)
- Topics: c, cplusplus, cpp, interop, nim, nim-lang
- Language: Nim
- Homepage:
- Size: 65.4 KB
- Stars: 76
- Watchers: 2
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# cinterop

A C/C++ interop library for the [Nim](https://nim-lang.org/) programming
language.This project was directly inspired by the [nimline](https://github.com/sinkingsugar/nimline)
library.## Overview
Similar to nimline, this library allows one to interop with C/C++ code without
having to create wrappers. Unlike nimline, cinterop does not depend on Nim's
experimental [dotOperators](https://nim-lang.org/docs/manual_experimental.html#special-operators)
feature and relies only on Nim's macro system to generate code.Features include:
* No dependencies other than Nim's standard library.
* Convenience macros to declare C/C++ types and functions ([decls.nim](src/cinterop/decls.nim)).
* Conversion of a subset of Nim to its syntactical equivalent in C/C++ without
requiring forward declarations ([exprs.nim](src/cinterop/exprs.nim)).This project **is not** a replacement for hand-written wrappers or wrapper
generators like [c2nim](https://github.com/nim-lang/c2nim). This library is
useful for **quickly prototyping** new code that depends on large C/C++
libraries, and is carefully designed so code can progressively be migrated to
use Nim's [`header`](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-header-pragma)
and [`importcpp`](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-importcpp-pragma)
pragmas directly.## Recommended compiler switches
This project uses Nim's [ARC](https://nim-lang.org/blog/2020/10/15/introduction-to-arc-orc-in-nim.html)
and C++17. It is well tested with non-trivial code using the Visual Studio
compiler on Windows. The recommended compiler switches are indicated at the top
of the main [test](tests/tcinterop.nim) file.## Showcase
Please **see [tests](tests/)** for examples of most features. This section
provides an incomplete summary of the core functionality.### C++ Class interop
Say you have the following C++ class:
```cpp
// simple.hppclass CppClass1
{
public:
int field1 = 1;int method1(int arg)
{
return 1 + arg;
}
};
```You simply need to declare the C++ type and the source file it resides in:
```nim
# simple.nimimport cinterop/decls
csource "simple.hpp":
type CppClass1* = object of CClass
```and then you can access the fields and methods of that type:
```nim
# main.nimimport cinterop/exprs
var instance1 = CppClass1.init()
echo cexpr[cint]^instance1.field1 # prints "1"
cexpr[cint]^instance1.field1 = 2
echo cexpr[cint]^instance1.method1(instance1.field1) # prints "3"
```Notice that `cexpr[T]^` indicates the return type `T` of the whole expression,
and only needs to be used at the beginning. This means that types for members do
not need to be declared, as long as the type of the variable whose members are
accessed is known.### Void returns
For expressions that evaluate to `void`, one can use the `cexpr^` invocation,
which is shorthand for `cexpr[void]^`:```nim
cexpr^instance1.method1(0)
```### Type inference
If the type of a return value does not need to be known but is used in an
operation, one can use the `cauto^` invocation like so:```nim
cauto^instance1.field1 += 2
```### Binary operations
A `cexpr[T]^` invocation can appear on either side of a binary operation.
`cauto^` can only be used on the right-hand side unless the left-hand side is
also a `cauto^` invocation. Examples:```nim
cexpr[cint]^instance1.field1 += cexpr[cint]^instance1.field1cauto^instance1.field1 += cexpr[cint]^instance1.field1 # same as above
cauto^instance1.field1 += cauto^instance1.field1 # same as above
```### Free function interop
The following technique can be used for libraries with lots of functions that
don't hang off of classes:```nim
# glfw3.nimcsource &"{GLFW}/glfw3.h": # header file
type cglfw* {.cgen:"(glfw$1(@))".} = object of CClass
``````nim
# canvas.nim
...
cauto^cglfw.GetMouseButton(self.window, button) == 1
# generates something like `glfwGetMouseButton(self.window, button) == 1`
...
````cglfw` here serves as a namespace that is not visible in C++. The `cgen` pragma
tells the compiler how `cglfw.GetMouseButton(self.window, button)` should be
generated and has the same semantics as Nim's `importcpp` pragma.### Enums
For working with C/C++ enums, one can use the `cenum` pragma like so:
```nim
type CPP_ENUM* {.cenum.} = object
```Although enums can be emulated using `CClass` and static methods, `cenum`
provides better checking of enum semantics and produces better C/C++ code for
enums. By default, enum field access results in an expansion similar to the
following:```nim
let enumValue = cauto^CPP_ENUM.MEMBER_1
# generates `CPP_ENUM enumValue = CPP_ENUM_MEMBER_1`
```Custom code generation for enums can be achieved using `cgen` as well.
## Gotchas
### Unary operations
Unary operators cannot be used with `cexpr[T]^` and `cauto^` invocations
without using parentheses:```nim
# echo -cauto^instance1.field1 # errorecho -(cauto^instance1.field1) # compiles
```There is a [proposal](https://github.com/nim-lang/RFCs/issues/415) to avoid this
issue and enable a more natural implementation of `cexpr[T]^`.### Initialization
`cauto^` can be used on the right-hand side of an initialization, but doing so
may cause backend compile errors, especially if done at the global scope:```nim
let value = cauto^instance1.field1 # C++ backend may produce an error here
```If this issue is encountered, the workaround is to explicitly specify the type:
```nim
let value = cexpr[cint]^instance1.field1
```Other issues are documented in the tests.
## Installing
Thanks to [@mantielero](https://github.com/mantielero) for adding initial
support for nimble! The package can be installed by following the nimble
instructions [here](https://github.com/nim-lang/nimble#nimble-install).## Usage
Typical usage is to import `cinterop/decls` in modules that declare C/C++ types,
and to import those modules along with `cinterop/exprs` to make use of them in
other modules.## Contributing
This project is maintained during my free time, and serves as a tool for a game
engine I am writing after work hours. Contributions are welcome, and I will
merge them immediately if they serve to keep the project robust, simple, and
maintainable.**Cheers and happy coding!** 🍺