# C API Guidelines

The C API uses a handle based approach for accessing the Qualcomm® Neural Processing SDK
API classes.

Handle Creation

The user can generate a handle for a specific class using the
**Create** function call. The Create call is similar to
creating an object in C++ by passing arguments to a
constructor. In the below code excerpt, the
Snpe\_SNPEBuilder\_Create function call creates a snpe builder
object and returns back a handle, which can then be used by the
user to invoke the builder object.

Snpe_SNPEBuilder_Handle_t snpeBuilderHandle = Snpe_SNPEBuilder_Create(containerHandle);
    Copy to clipboard

Handle Deletion

The lifecycle of handles created and returned to the user needs
to be managed by the user. i.e Every handle created by the
user or returned to the user by value needs to be deleted
explicitly by the user. The following code excerpt, shows how
the **Delete** call can be invoked by the user

Snpe_IUserBuffer_Handle_t userBufferEncodingFloatHandle = Snpe_UserBufferEncodingFloat_Create();
    Snpe_UserBufferEncodingFloat_Delete(userBufferEncodingFloatHandle);
    Copy to clipboard

Types of handles

There are three types of handles in the C API:

1. **Handle Created by User:** The User can invoke the
**Create** method to generate a handle to access the object
for a specific class. The handles generated using the create
call needs to be deleted explicitly by the user by calling
the corresponding **Delete** API call.

Snpe_RuntimeList_Handle_t runtimeListHandle = Snpe_RuntimeList_Create();
        Snpe_RuntimeList_Delete(runtimeListHandle);
        Copy to clipboard
2. **Handle returned by Value:** When an API returns back a
handle of a different type, the lifecycle of the handle is
to be managed by the user. The following excerpt
demonstrates the return by value scenario.

Snpe_StringList_Handle_t sl = Snpe_UserBufferMap_GetUserBufferNames(ubMapHandle);
        Snpe_StringList_Delete(sl);
        Copy to clipboard
3. **Handle returned by Reference:** When a handle is returned
by an API as reference, the handle is implicitly deleted
when the parent handle is deleted. So the user does not have
to manage the lifecycle in this case. The API returning a
handle by ref is denoted by **\_Ref**. In the following code
excerpt the ubHandle is deleted when the Delete API is
called on the ubMapHandle.

Snpe_IUserBuffer_Handle_t ubHandle = Snpe_UserBufferMap_GetUserBuffer_Ref(ubMapHandle, name);
        Snpe_UserBufferMap_Delete(ubMapHandle);
        Copy to clipboard

Porting C++ APIs to C

The C API functions follow the pattern
“Snpe\_&lt;ClassName&gt;\_&lt;function&gt;(&lt;objectHandle&gt;, …)”. As the C
API uses a handle based approach, while translating the C++
APIs, the handle to access the object needs to be passed as an
argument. The following code snippets demonstrates the
translation of Qualcomm® Neural Processing SDK C++ API to C API.

C++ API calls to create a userbuffer Map:

void createUserBuffer(zdl::DlSystem::UserBufferMap& userBufferMap,
                          std::unordered_map<std::string, std::vector<uint8_t>>& applicationBuffers,
                          std::vector<std::unique_ptr<zdl::DlSystem::IUserBuffer>>& snpeUserBackedBuffers,
                          std::unique_ptr<zdl::SNPE::SNPE>& snpe,
                          const char * name)
    {
       // get attributes of buffer by name
       auto bufferAttributesOpt = snpe->getInputOutputBufferAttributes(name);
       if (!bufferAttributesOpt) throw std::runtime_error(std::string("Error obtaining attributes for input tensor ") + name);
       // calculate the size of buffer required by the input tensor
       const zdl::DlSystem::TensorShape& bufferShape = (*bufferAttributesOpt)->getDims();
       // Calculate the stride based on buffer strides, assuming tightly packed.
       // Note: Strides = Number of bytes to advance to the next element in each dimension.
       // For example, if a float tensor of dimension 2x4x3 is tightly packed in a buffer of 96 bytes, then the strides would be (48,12,4)
       // Note: Buffer stride is usually known and does not need to be calculated.
       std::vector<size_t> strides(bufferShape.rank());
       strides[strides.size() - 1] = sizeof(float);
       size_t stride = strides[strides.size() - 1];
       for (size_t i = bufferShape.rank() - 1; i > 0; i--)
       {
          stride *= bufferShape[i];
          strides[i-1] = stride;
       }
       const size_t bufferElementSize = (*bufferAttributesOpt)->getElementSize();
       size_t bufSize = calcSizeFromDims(bufferShape.getDimensions(), bufferShape.rank(), bufferElementSize);
       // set the buffer encoding type
       zdl::DlSystem::UserBufferEncodingFloat userBufferEncodingFloat;
       // create user-backed storage to load input data onto it
       applicationBuffers.emplace(name, std::vector<uint8_t>(bufSize));
       // create Qualcomm (R) Neural Processing SDK user buffer from the user-backed buffer
       zdl::DlSystem::IUserBufferFactory& ubFactory = zdl::SNPE::SNPEFactory::getUserBufferFactory();
       snpeUserBackedBuffers.push_back(ubFactory.createUserBuffer(applicationBuffers.at(name).data(),
                                                                  bufSize,
                                                                  strides,
                                                                  &userBufferEncodingFloat));
       // add the user-backed buffer to the inputMap, which is later on fed to the network for execution
       userBufferMap.add(name, snpeUserBackedBuffers.back().get());
    Copy to clipboard

C API calls to create a userbuffer Map:

void createUserBuffer(Snpe_UserBufferMap_Handle_t userBufferMapHandle,
                          std::unordered_map<std::string, std::vector<uint8_t>>& applicationBuffers,
                          std::vector<Snpe_IUserBuffer_Handle_t> snpeUserBackedBuffersHandle,
                          Snpe_SNPE_Handle_t snpeHandle,
                          const char * name)
    {
       // get attributes of buffer by name
       Snpe_IBufferAttributes_Handle_t bufferAttributesOptHandle = Snpe_SNPE_GetInputOutputBufferAttributes(snpeHandle, name);
       if (bufferAttributesOptHandle == nullptr) throw std::runtime_error(std::string("Error obtaining attributes for input tensor ") + name);
       // calculate the size of buffer required by the input tensor
       Snpe_TensorShape_Handle_t bufferShapeHandle = Snpe_IBufferAttributes_GetDims(bufferAttributesOptHandle);
       // Calculate the stride based on buffer strides, assuming tightly packed.
       // Note: Strides = Number of bytes to advance to the next element in each dimension.
       // For example, if a float tensor of dimension 2x4x3 is tightly packed in a buffer of 96 bytes, then the strides would be (48,12,4)
       // Note: Buffer stride is usually known and does not need to be calculated.
       std::vector<size_t> strides(Snpe_TensorShape_Rank(bufferShapeHandle));
       strides[strides.size() - 1] = sizeof(float);
       size_t stride = strides[strides.size() - 1];
       for (size_t i = Snpe_TensorShape_Rank(bufferShapeHandle) - 1; i > 0; i--)
       {
          stride *= Snpe_TensorShape_At(bufferShapeHandle, i);
          strides[i-1] = stride;
       }
       Snpe_TensorShape_Handle_t stridesHandle = Snpe_TensorShape_CreateDimsSize(strides.data(), Snpe_TensorShape_Rank(bufferShapeHandle));
       size_t bufferElementSize = Snpe_IBufferAttributes_GetElementSize(bufferAttributesOptHandle);
       size_t bufSize = calcSizeFromDims(Snpe_TensorShape_GetDimensions(bufferShapeHandle), Snpe_TensorShape_Rank(bufferShapeHandle), bufferElementSize);
       // set the buffer encoding type
       Snpe_UserBufferEncoding_Handle_t userBufferEncodingFloatHandle = Snpe_UserBufferEncodingFloat_Create();
       // create user-backed storage to load input data onto it
       applicationBuffers.emplace(name, std::vector<uint8_t>(bufSize));
       // create Qualcomm (R) Neural Processing SDK user buffer from the user-backed buffer
       ubsHandle.push_back(Snpe_Util_CreateUserBuffer(applicationBuffers.at(name).data(),
                                                      bufSize,
                                                      stridesHandle,
                                                      userBufferEncodingFloatHandle));
       // add the user-backed buffer to the inputMap, which is later on fed to the network for execution
       Snpe_UserBufferMap_Add(userBufferMapHandle, name, snpeUserBackedBuffersHandle.back());
       // clean up created handles
       Snpe_IBufferAttributes_Delete(bufferAttributesOptHandle);
       Snpe_UserBufferEncodingFloat_Delete(userBufferEncodingFloatHandle);
       Snpe_TensorShape_Delete(bufferShapeHandle);
    Copy to clipboard

Last Published: Jun 04, 2026

[Previous Topic
C Tutorial - Build the Sample](https://docs.qualcomm.com/bundle/publicresource/80-63442-10/topics/c_tutorial.md) [Next Topic
Android Tutorial](https://docs.qualcomm.com/bundle/publicresource/80-63442-10/topics/android_tutorial.md)