# C Tutorial - Build the Sample Prerequisites - The Qualcomm® Neural Processing SDK has been set up following the [Qualcomm (R) Neural Processing SDK Setup](https://docs.qualcomm.com/doc/80-63442-2/topic/setup.html) chapter. - The [Tutorials Setup](https://docs.qualcomm.com/doc/80-63442-2/topic/tutorial_setup.html) has been completed. Introduction This tutorial demonstrates how to build a C sample application that can execute neural network models on the PC or target device. Please note, while this sample code does not do any error checking, it is strongly recommended that users check for errors when using the Qualcomm® Neural Processing SDK APIs. Most applications will follow the following pattern while using a neural network: 1. [Get Available Runtime](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#get-available-runtime) 2. [Load Network](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#load-network) 3. [Set Network Builder Options](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#set-network-builder-options) 4. [Load Network Inputs](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#load-network-inputs) 1. [Using User Buffers](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#load-using-user-buffers) 2. [Using ITensors](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#load-using-itensors) 5. [Execute the Network & Process Output](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#execute-the-network-process-output) 1. [Using User Buffers](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#execute-using-user-buffers) 2. [Using ITensors](https://docs.qualcomm.com/doc/80-63442-2/topic/c_tutorial.html#execute-using-itensors) The below snippet of code provides an overall idea of how to use Qualcomm(R)| Neural Processing SDK APIs. For working example code, please refer to the collection of sample apps located at $SNPE\_ROOT/examples/SNPE/NativeCpp/SampleCode\_CAPI/ bool useUserSuppliedBuffers = true; int inputListNum = 1; //Create runtime Snpe_Runtime_t runtime = checkRuntime(); //Add runtime to runtime list Snpe_RuntimeList_Handle_t runtimeListHandle = Snpe_RuntimeList_Create(); Snpe_RuntimeList_Add(runtimeListHandle, runtime); //Load dlc file Snpe_DlContainer_Handle_t containerHandle = loadContainerFromFile(dlc); //Generate snpe handle from builder options Snpe_SNPE_Handle_t snpeHandle = setBuilderOptions(containerHandle, runtimeListHandle, useUserSuppliedBuffers); Snpe_TensorMap_Handle_t inputTensorMapHandle = loadInputTensor(snpeHandle, fileLine); // ITensor loadInputUserBuffer(applicationInputBuffers, snpeHandle, fileLine); // User Buffer executeNetwork(snpeHandle , inputTensorMapHandle, OutputDir, inputListNum); // ITensor executeNetwork(snpeHandle, inputMapHandle, outputMapHandle, applicationOutputBuffers, OutputDir, inputListNum); // User Buffer Copy to clipboard The sections below describe how to implement each step described above. Get Available Runtime The code excerpt below illustrates how to check if a specific runtime is available using the native APIs (the GPU runtime is used as an example). Snpe_Runtime_t checkRuntime() { Snpe_DlVersion_Handle_t versionHandle = Snpe_Util_GetLibraryVersion(); Snpe_Runtime_t Runtime; std::cout << "Qualcomm (R) Neural Processing SDK Version: " << Snpe_DlVersion_ToString(versionHandle) << std::endl; //Print Version number Snpe_DlVersion_Delete(versionHandle); if (Snpe_Util_IsRuntimeAvailable(SNPE_RUNTIME_GPU)) { Runtime = SNPE_RUNTIME_GPU; } else { Runtime = SNPE_RUNTIME_CPU; } return Runtime; } Copy to clipboard Load Network The code excerpt below illustrates how to load a network from the Qualcomm® Neural Processing SDK container file (DLC). Snpe_DlContainer_Handle_t loadContainerFromFile(std::string containerPath) { Snpe_DlContainer_Handle_t containerHandle = Snpe_DlContainer_Open(containerPath.c_str()); return containerHandle; } Copy to clipboard Set Network Builder Options The following code demonstrates how to instantiate a SNPE Builder object, which will be used to execute the network with the given parameters. Snpe_SNPE_Handle_t setBuilderOptions(Snpe_DlContainer_Handle_t containerHandle, Snpe_RuntimeList_Handle_t runtimeListHandle, bool useUserSuppliedBuffers) { Snpe_SNPE_Handle_t snpeHandle; Snpe_SNPEBuilder_Handle_t snpeBuilderHandle = Snpe_SNPEBuilder_Create(containerHandle); Snpe_SNPEBuilder_SetRuntimeProcessorOrder(snpeBuilderHandle, runtimeListHandle) Snpe_SNPEBuilder_SetUseUserSuppliedBuffers(snpeBuilderHandle, useUserSuppliedBuffers) snpeHandle = Snpe_SNPEBuilder_Build(snpeBuilderHandle); return snpeHandle; } Copy to clipboard Load Network Inputs Network inputs and outputs can be either user-backed buffers or ITensors (built-in Qualcomm® Neural Processing SDK buffers), but not both. The advantage of using user-backed buffers is that it eliminates an extra copy from user buffers to create ITensors. Both methods of loading network inputs are shown below. Using User Buffers Qualcomm® Neural Processing SDK can create its network inputs and outputs from user-backed buffers. Note that Qualcomm® Neural Processing SDK expects the values of the buffers to be present and valid during the duration of its execution. Here is a function for creating a Qualcomm® Neural Processing SDK UserBuffer from a user-backed buffer and storing it in a UserBufferMap. These maps are a convenient collection of all input or output user buffers that can be passed to Qualcomm® Neural Processing SDK to execute the network. Disclaimer: The strides of the buffer should already be known by the user and should not be calculated as shown below. The calculation shown is solely used for executing the example code. 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()); } Copy to clipboard The following function then shows how to load input data from file(s) to user buffers. Note that the input values are simply loaded onto user-backed buffers, on top of which Qualcomm® Neural Processing SDK can create Qualcomm® Neural Processing SDK UserBuffers, as shown above. void loadInputUserBuffer(std::unordered_map>& applicationBuffers, Snpe_SNPE_Handle_t snpeHandle, const std::string& fileLine) { // get input tensor names of the network that need to be populated Snpe_StringList_Handle_t inputNamesHandle = Snpe_SNPE_GetInputTensorNames(snpeHandle); if (inputNamesHandle == nullptr) throw std::runtime_error("Error obtaining input tensor names"); assert(Snpe_StringList_Size(inputNamesHandle) > 0); // treat each line as a space-separated list of input files std::vector filePaths; split(filePaths, fileLine, ' '); if (Snpe_StringList_Size(inputNamesHandle)) std::cout << "Processing DNN Input: " << std::endl; for (size_t i = 0; i < Snpe_StringList_Size(inputNamesHandle); i++) { const char* name = Snpe_StringList_At(inputNamesHandle, i); std::string filePath(filePaths[i]); // print out which file is being processed std::cout << "\t" << i + 1 << ") " << filePath << std::endl; // load file content onto application storage buffer, // on top of which, Qualcomm (R) Neural Processing SDK has created a user buffer loadByteDataFile(filePath, applicationBuffers.at(name)); }; } Copy to clipboard Using ITensors Snpe_TensorMap_Handle_t loadInputTensor (Snpe_SNPE_Handle_t snpeHandle, std::string& fileLine) { Snpe_ITensor_Handle_t input; Snpe_StringList_Handle_t strListHandle = Snpe_SNPE_GetInputTensorNames(snpeHandle); if (strListHandle == nullptr) throw std::runtime_error("Error obtaining Input tensor names"); // Make sure the network requires only a single input assert (Snpe_StringList_Size(strListHandle) == 1); // If the network has a single input, each line represents the input file to be loaded for that input std::string filePath(fileLine); std::cout << "Processing DNN Input: " << filePath << "\n"; std::vector inputVec = loadFloatDataFile(filePath); /* Create an input tensor that is correctly sized to hold the input of the network. Dimensions that have no fixed size will be represented with a value of 0. */ auto inputDimsHandle = Snpe_SNPE_GetInputDimensions(snpeHandle, Snpe_StringList_At(strListHandle, 0)); /* Calculate the total number of elements that can be stored in the tensor so that we can check that the input contains the expected number of elements. With the input dimensions computed create a tensor to convey the input into the network. */ input = Snpe_Util_CreateITensor(inputDimsHandle); /* Copy the loaded input file contents into the networks input tensor.SNPE's ITensor supports C++ STL functions like std::copy() */ std::copy(inputVec.begin(), inputVec.end(), (float*)Snpe_ITensor_GetData(input)); Snpe_TensorMap_Handle_t inputTensorMapHandle = Snpe_TensorMap_Create(); Snpe_TensorMap_Add(inputTensorMapHandle, Snpe_StringList_At(strListHandle, 0), inputs[i]); return inputTensorMapHandle; } Copy to clipboard Execute the Network & Process Output The following snippets of code use the native API to execute the network (in UserBuffer or ITensor mode) and show how to iterate through the newly populated output tensor. Using User Buffers void executeNetwork(Snpe_SNPE_Handle_t snpeHandle, Snpe_UserBufferMap_Handle_t inputMapHandle, Snpe_UserBufferMap_Handle_t outputMapHandle, std::unordered_map>& applicationOutputBuffers, const std::string& outputDir, int num) { // Execute the network and store the outputs in user buffers specified in outputMap Snpe_SNPE_ExecuteUserBuffers(snpeHandle, inputMapHandle, outputMapHandle); // Get all output buffer names from the network Snpe_StringList_Handle_t outputBufferNamesHandle = Snpe_UserBufferMap_GetUserBufferNames(outputMapHandle); // Iterate through output buffers and print each output to a raw file std::for_each(Snpe_StringList_Begin(outputBufferNamesHandle), Snpe_StringList_End(outputBufferNamesHandle), [&](const char* name) { std::ostringstream path; path << outputDir << "/Result_" << num << "/" << name << ".raw"; SaveUserBuffer(path.str(), applicationOutputBuffers.at(name)); }); } // The following is a partial snippet of the function void SaveUserBuffer(const std::string& path, const std::vector& buffer) { ... std::ofstream os(path, std::ofstream::binary); if (!os) { std::cerr << "Failed to open output file for writing: " << path << "\n"; std::exit(EXIT_FAILURE); } if (!os.write((char*)(buffer.data()), buffer.size())) { std::cerr << "Failed to write data to: " << path << "\n"; std::exit(EXIT_FAILURE); } } Copy to clipboard Using ITensors void executeNetwork(Snpe_SNPE_Handle_t snpeHandle, Snpe_TensorMap_Handle_t inputTensorMapHandle, std::string OutputDir, int num) { // Execute the network and store the outputs that were specified when creating the network in a TensorMap Snpe_TensorMap_Handle_t outputTensorMapHandle = Snpe_TensorMap_Create(); Snpe_SNPE_ExecuteITensors(snpeHandle, inputTensorMapHandle, outputTensorMapHandle); Snpe_StringList_Handle_t tensorNamesHandle = Snpe_TensorMap_GetTensorNames(outputTensorMapHandle); // Iterate through the output Tensor map, and print each output layer name std::for_each( Snpe_StringList_Begin(tensorNamesHandle), Snpe_StringList_End(tensorNamesHandle), [&](const char* name) { std::ostringstream path; path << OutputDir << "/" << "Result_" << num << "/" << name << ".raw"; auto tensorHandle = Snpe_TensorMap_GetTensor_Ref(outputTensorMapHandle, name); SaveITensor(path.str(), tensorHandle); }); // Clean up created handles Snpe_TensorMap_Delete(outputTensorMapHandle); Snpe_StringList_Delete(tensorNamesHandle); } // The following is a partial snippet of the function void SaveITensor(const std::string& path, Snpe_ITensor_Handle_t tensorHandle) { ... std::ofstream os(path, std::ofstream::binary); if (!os) { std::cerr << "Failed to open output file for writing: " << path << "\n"; std::exit(EXIT_FAILURE); } auto begin = static_cast(Snpe_ITensor_GetData(tensorHandle)); auto size = Snpe_ITensor_GetSize(tensorHandle); for ( auto it = begin; it != begin + size; ++it ) { float f = *it; if (!os.write(reinterpret_cast(&f), sizeof(float))) { std::cerr << "Failed to write data to: " << path << "\n"; std::exit(EXIT_FAILURE); } } } 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/cplus_plus_tutorial.md) [Next Topic C API Guidelines](https://docs.qualcomm.com/bundle/publicresource/80-63442-2/topics/c_api_guidelines.md)