# 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\_<ClassName>\_<function>(<objectHandle>, …)”. 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>& applicationBuffers, std::vector>& snpeUserBackedBuffers, std::unique_ptr& 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 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(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>& applicationBuffers, std::vector 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 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(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: Oct 02, 2025 [Previous Topic C Tutorial - Build the Sample](https://docs.qualcomm.com/bundle/publicresource/80-63442-2/topics/c_tutorial.md) [Next Topic Android Tutorial](https://docs.qualcomm.com/bundle/publicresource/80-63442-2/topics/android_tutorial.md)