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::Net and ncnn::Mat in src/net.h and src/mat.h provide idiomatic C++ interfaces.
  • C API: Exposes a C-compatible ABI via opaque handles (ncnn_net_t, ncnn_mat_t) defined in src/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.h use C++ features like classes and namespaces.
  • C API: Only needs a C compiler. The implementation in src/c_api.cpp wraps 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::Mat automatically releases memory in its destructor. ncnn::Net cleans up layers automatically when it goes out of scope.
  • C API: Requires manual lifecycle management. You must call ncnn_net_create/ncnn_net_destroy and ncnn_mat_create/ncnn_mat_destroy explicitly. The C implementation internally calls new Net() and delete net as shown in src/c_api.cpp.

Feature Exposure

  • C++ API: Full access to all class members, inline helpers, and overloads. For example, ncnn::Mat provides reshape, 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.h or src/mat.h breaks 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.cpp is 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.cpp and src/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 explicit ncnn_net_destroy() and ncnn_mat_destroy() calls as implemented in src/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →