C++


C/C++: Full example of using C++ code in a C project

The following set of code present a fully functioning example of using a simple CPP library as part of a C based project to print on screen.

[download id=”2428″]

The trick relies on encapsulating the CPP header definitions in the extern "C" declaration. extern "C" will make all function and variable names in C++ have C linkage. What this means at the compiler level is that the compiler will not modify the names so that the C code can link to them and use them using a C compatible header file containing just the declarations of your functions and variables.

[download id=”2428″]

 

main.c

#include "cpp_library.h"
#include "c_library.h"

int main() {

    cpp_hello_world();
    c_hello_world();
    return 0;
}

cpp_library.h

#ifndef C_BASE_CPP_LIBRARY_H
#define C_BASE_CPP_LIBRARY_H

#ifdef __cplusplus
extern "C" {
#endif

void cpp_hello_world();

#ifdef __cplusplus
}
#endif

#endif //C_BASE_CPP_LIBRARY_H

cpp_library.cpp

#include <iostream>
#include "cpp_library.h"

void cpp_hello_world() {

    std::cout << "Hello, World!" << std::endl;
}

c_library.h

#ifndef C_BASE_C_LIBRARY_H
#define C_BASE_C_LIBRARY_H

void c_hello_world();

#endif //C_BASE_C_LIBRARY_H

c_library.c

#include <stdio.h>
#include "c_library.h"

void c_hello_world() {

    printf("Hello, World!\n");
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.6)
project(C_Base)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

set(SOURCE_FILES main.c cpp_library.cpp cpp_library.h c_library.c c_library.h)
add_executable(C_Base ${SOURCE_FILES})

[download id=”2428″]


C/C++: Get the size of a file in bytes

The following function accepts the name of a file as a string and returns the size of the file in bytes. If for any reason it cannot get the file information, it will return the value -1.

[download id=”2417″]

In our header file, we used the following pre-processor directives around our declarations

#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif

to allow c++ code to call our c function.

The usage example code makes three tests:

  1. Getting the size of the currently executing binary, as it will have a non-zero size
  2. Getting the size of a non-existing file, to check that it will properly return -1
  3. Getting the size of an empty file, to be sure it is empty we create it right before the test

[download id=”2417″]

Source file (file_helpers.c)

#include <sys/stat.h>
#include "file_helpers.h"

//It will return the size of the file in bytes OR -1 in case that it cannot get any status information for it
off_t get_file_size(const char *filename) {
  //Specialised struct that can hold status attributes of files.
  struct stat st;

  //Gets file attributes for filename and puts them in the stat buffer.
  // Upon successful completion, it returns 0, otherwise and errno will be set to indicate the error.
  if (stat(filename, &st) == 0) {
    //Size of file, in bytes.
    return st.st_size;
  }

  return -1;
}

Header file (file_helpers.h)


#ifndef GM_S_LITTLE_HELPERS_FILE_HELPERS_H
#define GM_S_LITTLE_HELPERS_FILE_HELPERS_H

#ifdef __cplusplus
extern "C" {
#endif

off_t get_file_size(const char *filename);

#ifdef __cplusplus
}
#endif

#endif //GM_S_LITTLE_HELPERS_FILE_HELPERS_H

Usage example (main.cpp)

#include <iostream>
#include "file_helpers.h"

void print_file_size(const char *filename) {
  const off_t size_of_file = get_file_size(filename);
  if (size_of_file > 0) {
    printf("The size of '%s' is %zd bytes\n", filename, size_of_file);
  }
  else if (size_of_file == 0) {
    printf("The file '%s' is empty\n", filename);
  }
  else {
    printf("Could not get the status information for file '%s'\n", filename);
  }
}

int main(int argc, char *argv[]) {

  //Testing a non-zero sized file
  print_file_size(argv[0]);
  //Testing for a non-existing file
  print_file_size("some file that does not exist...");
  const char * filename = "/tmp/some_empty_file";
  //Creating an empty file
  FILE * fout = fopen(filename, "w");
  fclose(fout);
  //Testing for an empty file
  print_file_size(filename);

  return 0;
}

[download id=”2417″]


C/C++: Perform safe sprintf 1

The following function accepts the address of a char * buffer, the formatting string for printf along with all the parameters needed to fill the formatting string and updates the location of the buffer to point at the final formatted string.

[download id=”2418″]

This code does not require the user to perform malloc before filling in the buffer. Using vsnprintf (variation of snprintf for variable arguments) it will automatically find the correct size that the buffer should have, allocate the space, switch the pointer of the buffer and prepare the final string using the formatting arguments.

In our header file, we used the following pre-processor directives around our declarations

#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif

to allow c++ code to call our function.

[download id=”2418″]

Source file (string_helpers.c)


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "string_helpers.h"

int safe_sprintf(char ** buffer, const char *format, ...) {

  va_list arguments;
  //The va_start(va_list arguments, last) macro initializes and must be called first.
  //The argument last is the name of the last argument before the variable argument list, that is, the last argument of which the calling function knows the type.
  va_start (arguments, format);

  //Upon successful return, vsnprintf returns the number of characters printed (excluding the null byte used to end output to strings).
  //For that reason we add one at the end of the length.
  const int length = vsnprintf(NULL, 0, format, arguments) + 1;

  //Each invocation of va_start() must be matched by a corresponding invocation of va_end() in the same function.
  // After the call va_end(arguments) the variable arguments is undefined.
  // Multiple traversals of the list, each bracketed by va_start() and va_end() are possible. va_end() may be a macro or a function.
  va_end (arguments);

  if (*buffer) {
    free(*buffer);
  }
  if (!(*buffer = malloc(length * sizeof(char)))) {
    return EXIT_FAILURE;
  }

  va_start(arguments, format);
  vsnprintf(*buffer, (size_t) length, format, arguments);
  va_end (arguments);

  return EXIT_SUCCESS;
}

Header file (string_helpers.h)


#ifndef GM_S_LITTLE_HELPERS_STRING_HELPERS_H
#define GM_S_LITTLE_HELPERS_STRING_HELPERS_H

#ifdef __cplusplus
extern "C" {
#endif

int safe_sprintf(char ** buffer, const char *format, ...);

#ifdef __cplusplus
}
#endif

#endif //GM_S_LITTLE_HELPERS_STRING_HELPERS_H

Usage example (main.cpp)


#include <iostream>
#include "string_helpers.h"

int main() {

  char * buffer;
  safe_sprintf(&buffer, "Hello, World!\nFrom Line %d in function %s of the file %s.", __LINE__, __func__, __FILE__);
  printf("%s", buffer);
  return 0;
}

[download id=”2418″]


C/C++: Set Affinity to process thread – Example Code 3

The following code sets the affinity of the process thread to a specific CPU core.
In this example, we define the CPU core id using the variable core_id.

Full source code available here [download id=”2363″]


#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU
#include <sched.h>
#include <errno.h>
#include <unistd.h>

// The <errno.h> header file defines the integer variable errno, which is set by system calls and some library functions in the event of an error to indicate what went wrong.
#define print_error_then_terminate(en, msg) \
  do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)


int main(int argc, char *argv[]) {

  // We want to camp on the 2nd CPU. The ID of that core is #1.
  const int core_id = 1;
  const pid_t pid = getpid();

  // cpu_set_t: This data set is a bitset where each bit represents a CPU.
  cpu_set_t cpuset;
  // CPU_ZERO: This macro initializes the CPU set set to be the empty set.
  CPU_ZERO(&cpuset);
  // CPU_SET: This macro adds cpu to the CPU set set.
  CPU_SET(core_id, &cpuset);

  // sched_setaffinity: This function installs the cpusetsize bytes long affinity mask pointed to by cpuset for the process or thread with the ID pid. If successful the function returns zero and the scheduler will in future take the affinity information into account. 
  const int set_result = sched_setaffinity(pid, sizeof(cpu_set_t), &cpuset);
  if (set_result != 0) {

    print_error_then_terminate(set_result, "sched_setaffinity");
  }

  // Check what is the actual affinity mask that was assigned to the thread.
  // sched_getaffinity: This functions stores the CPU affinity mask for the process or thread with the ID pid in the cpusetsize bytes long bitmap pointed to by cpuset. If successful, the function always initializes all bits in the cpu_set_t object and returns zero.
  const int get_affinity = sched_getaffinity(pid, sizeof(cpu_set_t), &cpuset);
  if (get_affinity != 0) {

    print_error_then_terminate(get_affinity, "sched_getaffinity");
  }

  // CPU_ISSET: This macro returns a nonzero value (true) if cpu is a member of the CPU set set, and zero (false) otherwise. 
  if (CPU_ISSET(core_id, &cpuset)) {

    fprintf(stdout, "Successfully set thread %d to affinity to CPU %d\n", pid, core_id);
  } else {

    fprintf(stderr, "Failed to set thread %d to affinity to CPU %d\n", pid, core_id);
  }

  return 0;
}

To compile we used the following command

gcc -Wall affinity.c -o affinity;

Full source code available here [download id=”2363″]

For a full pthread example please visit this link.