Going Native - Calling Native Code

For a long time I've been interested in methods to call native code from modern languages. Ever since I started working with Java I heard about something called JNI and that it could be used to unlock ancient mysteries hidden away in native code. At the end of this summer I had some time and finally decided to give it a try.

This will be a series of articles on calling native code (C and/or C++) from modern development languages. For now that means Java, Python, and C#. This post introduces the native library I wrote to demonstrate this process, while follow-on posts will demonstrate calling it from other languages.

The native code

Another subject I've been interested in for a long time is linear algebra, specifically matrices and their uses. To try to get a better understanding of them I wrote a couple of small C libraries using them. My linear algebra guide is Linear Algebra and Its Applications (3rd ed) by Gilbert Strang.

One is a basic library to handle the arithmetic of rational numbers: Rashunal.

The other is a library to handle matrices of Rashunals and some basic operations on them (add, multiply, row operations, Gauss factoring). Todos include matrix inversion, minors, calculation of determinants. Finding eigenvalues and eigenvectors requires solving high-degree polynmial equations, so I'm not sure I'll get to that. RMatrix.

I wrote these to the best of my ability using CMake, Unity, and Valgrind. They should be cross-platform and installable on any system (Linux,MacOS, Windows). They have basic unit tests and have been checked for memory integrity. However, they should not be used for any production environment. Do not build your great AI model or statistics package on them!

Prerequisites and Setup

  • CMake
  • Make - Installation is different on different systems
    • Linux - almost certainly available on your distribution's package installer
      • Debian - sudo apt update && sudo apt install make
      • Red Hat - sudo yum update && sudo yum install make
    • MacOS - brew install make
    • Windows - choco install make
  • Your favorite C compiler

Clone the repositories above. The READMEs have instructions on how to compile and install the library code. Briefly:

1$ cd Rashunal
2$ cmake -S . -B build
3$ cd build
4$ make && make test && make install
5$ cd ..
6$ cd rmatrix
7$ cmake -S . -B build
8$ cd build
9$ make && make test && make install

Approach

In each language I target, I'd like to write an application that can read a data file representing a matrix of rational numbers, send it to the native code, do something fairly complicated with it, and return it to the caller in some useful form. To demonstrate I wrote a model application in the RMatrix repository. It reads a matrix in a simple space-delimited format, converts it to an RMatrix, factors it into P-1, L, D, and U matrices, and displays the L and U matrices in the terminal window.

example.txt:

1-2  1/3 -3/4
2 6 -1    8
3 8  3/2 -7
 1$ cd driver
 2$ make
 3$ cat example.txt | ./driver
 4Height is 3
 5Width is 3
 6L:
 7[  1 0 0 ]
 8[ -3 1 0 ]
 9[ -4 0 1 ]
10U:
11[ 1 -1/6  3/8  ]
12[ 0  1   60/17 ]
13[ 0  0    1    ]

Next steps

In further entries in this series I'll demonstrate calling this library from several modern, widely-used, high-level languages. The ones I'm planning to target include:

  • Java
  • C#
  • Python
  • (maybe) Swift

But who knows how deep this rabbit hole will go?

This post was originally hosted at https://the-solitary-programmer.blogspot.com/2025/09/going-native-calling-native-code.html.

Posts in this series