ncnn C API vs C++ API: When to Use Each Interface
Choose the ncnn C++ API for native C++ projects requiring modern features like RAII and templates, and select the ncnn C API when building cross-language bindings or working in pure C environments that must avoid C++ runtime dependencies.
Tencent's ncnn is a high-performance neural network inference framework optimized for mobile and edge devices. While the library is implemented in modern C++, it provides a thin ncnn C API wrapper that offers identical runtime performance with different ergonomics. Understanding when to use each interface depends on your project's language constraints, build environment, and integration requirements.
Key Differences Between ncnn C API and C++ API
The primary distinction lies in language integration and memory management models. Both APIs ultimately call the same core engine code in src/net.cpp and src/mat.cpp, so inference performance is identical.
Language Integration
- C++ API: Native to C++ projects. Supports templates, function overloads, STL containers, and exception handling. Classes like
ncnn::Netandncnn::Matinsrc/net.handsrc/mat.hprovide idiomatic C++ interfaces. - C API: Exposes a C-compatible ABI via opaque handles (
ncnn_net_t,ncnn_mat_t) defined insrc/c_api.h. Callable from pure C, Python ctypes, Rust FFI, Go, and Java JNI.
Compilation Requirements
- C++ API: Requires a C++ compiler and the C++ standard library. Headers in
src/net.huse C++ features like classes and namespaces. - C API: Only needs a C compiler. The implementation in
src/c_api.cppwraps C++ objects but exposes only C linkage, eliminating name-mangling issues and C++ runtime dependencies.
Memory Management
- C++ API: Uses RAII (Resource Acquisition Is Initialization).
ncnn::Matautomatically releases memory in its destructor.ncnn::Netcleans up layers automatically when it goes out of scope. - C API: Requires manual lifecycle management. You must call
ncnn_net_create/ncnn_net_destroyandncnn_mat_create/ncnn_mat_destroyexplicitly. The C implementation internally callsnew Net()anddelete netas shown insrc/c_api.cpp.
Feature Exposure
- C++ API: Full access to all class members, inline helpers, and overloads. For example,
ncnn::Matprovidesreshape,clone, and pixel conversion methods directly. - C API: Exposes a subset of functionality most useful across language borders—primarily network creation, parameter loading, and tensor extraction. Advanced features require C++ API access.
ABI Stability
- C++ API: Changing class layout in
src/net.horsrc/mat.hbreaks binary compatibility; consumers must recompile. - C API: Designed for stable binary interface. The layout of opaque structs never changes; only the implementation in
src/c_api.cppis modified.
When to Choose the ncnn C++ API
Select the C++ API when your project is built with C++ and you want maximum ergonomics and feature completeness.
Native C++ Development
If your codebase is C++-only, the C++ API provides idiomatic syntax that integrates seamlessly with modern C++ features. You can use ncnn::Net and ncnn::Mat directly from src/net.h and src/mat.h, leverage STL containers to manage multiple networks, and rely on RAII for automatic memory management without explicit cleanup calls.
Advanced Features and Ergonomics
The C++ API exposes the full surface area of the library. You can access Vulkan pipeline configurations through ncnn::Option, implement custom layer factories by inheriting from ncnn::Layer, and use template-based utilities. Method overloading allows convenient pixel format conversions via ncnn::Mat::from_pixels_resize, which would require multiple explicit function calls in the C API.
When to Choose the ncnn C API
Use the C API when you need language interoperability or must avoid C++ toolchain dependencies.
Cross-Language Bindings
The C API is the foundation for creating bindings in other languages. Because src/c_api.h exposes opaque handles (ncnn_net_t, ncnn_mat_t) with C linkage, you can interface with Python using ctypes, with Rust via FFI, or with Go using cgo. The implementation in src/c_api.cpp handles the translation between C handles and C++ objects (e.g., ncnn_net_create calls new Net()), allowing other languages to leverage ncnn's optimized inference engine without writing C++ wrapper code.
C-Only Build Environments
If your project must compile with a pure C toolchain or you need to minimize binary footprint by avoiding C++ runtime libraries, the C API is the correct choice. The src/c_api.cpp wrapper compiles the C++ implementation into a library that presents only a C interface, eliminating name-mangling issues and C++ standard library dependencies in the consuming application. This is critical for embedded systems with limited storage or when integrating with legacy C codebases.
Code Examples
C++ API Implementation
The following example demonstrates idiomatic C++ usage with RAII-based resource management. This code uses ncnn::Net and ncnn::Mat from src/net.h and src/mat.h:
#include <ncnn/net.h>
int main() {
ncnn::Net net;
// Configure options directly on the object
net.opt.num_threads = 4;
// Load model parameters and weights
net.load_param("mobilenetv2.param");
net.load_model("mobilenetv2.bin");
// Create input tensor using static factory method
ncnn::Mat in = ncnn::Mat::from_pixels_resize(
image_data, ncnn::Mat::PIXEL_BGR, img_w, img_h, 224, 224);
// Inference with automatic resource cleanup
ncnn::Extractor ex = net.create_extractor();
ex.input("data", in);
ncnn::Mat out;
ex.extract("prob", out);
// Resources automatically freed when objects go out of scope
return 0;
}
C API Implementation
This example shows the explicit lifecycle management required when using the C API from src/c_api.h. Note how every create call requires a corresponding destroy call:
#include "c_api.h"
int main() {
// Explicit creation of opaque handles
ncnn_net_t net = ncnn_net_create();
// Configure options through getter/setter functions
ncnn_option_t opt = ncnn_net_get_option(net);
ncnn_option_set_num_threads(opt, 4);
ncnn_net_set_option(net, opt);
// Load model data
ncnn_net_load_param(net, "mobilenetv2.param");
ncnn_net_load_model(net, "mobilenetv2.bin");
// Create extractor and input tensor
ncnn_extractor_t ex = ncnn_extractor_create(net);
ncnn_mat_t in = ncnn_mat_create();
// ... populate input data ...
ncnn_extractor_input(ex, "data", in);
ncnn_mat_t out;
ncnn_extractor_extract(ex, "prob", &out);
// Manual cleanup required for every created object
ncnn_mat_destroy(in);
ncnn_mat_destroy(out);
ncnn_extractor_destroy(ex);
ncnn_net_destroy(net);
return 0;
}
Summary
- Runtime performance is identical between the C++ and C APIs because both call the same core engine implementation in
src/net.cppandsrc/mat.cpp. - Choose the C++ API when working in C++ environments to benefit from RAII automatic memory management, STL integration, and full access to advanced features like custom layer factories and Vulkan configurations.
- Choose the C API when building language bindings for Python, Rust, or Go, or when compiling in pure C environments that must avoid C++ runtime dependencies and name-mangling issues.
- Memory management differs significantly: C++ uses destructors (
~Net(),~Mat()), while C requires explicitncnn_net_destroy()andncnn_mat_destroy()calls as implemented insrc/c_api.cpp.
Frequently Asked Questions
Is the ncnn C API slower than the C++ API?
No, the ncnn C API provides identical inference performance to the C++ API because it is a thin wrapper that forwards calls to the same underlying C++ objects. The implementation in src/c_api.cpp simply translates C handles (like ncnn_net_t) into C++ object pointers (like Net*) and invokes the native methods, adding negligible overhead.
Can I mix C++ and C API calls in the same project?
Yes, you can mix both APIs in the same application because they operate on the same underlying data structures. The C API opaque handles (defined in src/c_api.h) are essentially pointers to C++ objects, so you can create a network with ncnn::Net and pass its internal pointer to C functions, or vice versa, provided you manage object lifetimes carefully to avoid double-free errors.
Which API should I use for Python bindings?
You should use the ncnn C API for Python bindings because Python's ctypes module can directly interface with C functions and opaque pointers without requiring C++ compiler tooling. The C API in src/c_api.h exposes functions like ncnn_net_create() and ncnn_extractor_extract() that map cleanly to Python function calls, whereas binding to the C++ API would require complex wrapper generators like pybind11 or SWIG.
Does the ncnn C API support Vulkan GPU acceleration?
Yes, the ncnn C API supports Vulkan GPU acceleration because it exposes the same ncnn_option_t configuration structure used to enable GPU inference. You can call ncnn_option_set_use_vulkan_compute(opt, 1) on the option object retrieved via ncnn_net_get_option(), allowing the underlying C++ engine to utilize Vulkan pipelines exactly as it would through the C++ API's opt.use_vulkan_compute flag.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →