Logging library — c calls c++

In order to meet the requirements of automobile safety level, the log module needs to call the log module in autosar. So we are faced with two problems at this time:

1. Retain the original way of implementing log printing through macro definition.

2. The autosar module is C++ code. How does C call this module to print logs?

3. How to meet misra coding requirements most conveniently

Solution:

The C++ implementation is very simple. Log printing is implemented through a macro definition. The macro definition function contains a printing function, which calls the class object that encapsulates the autosar module to implement printing.

Note: The macro definition function actually does not meet the requirements of misra, but in order to retain the original code style, the output method is retained. Another very important reason is because the file and line number of the log need to be printed. If it is an ordinary function, the user I have to pass in the corresponding information each time, and the file line number printing of the macro definition function can be encapsulated and implemented by me!

So what about the C module and the call, the callback function is used here.

Let’s go straight to the code! A simple demo

log_c++.h

#ifndef LOG_H_
#define LOG_H_
#include <cstdint>
#include <string>
#include <utility>


/* polyspace + 20 MISRA-CPP:16-2-1 MISRA-CPP:16-2-2 MISRA-CPP:16-0-4 [Justified:Unset] "Comment xxxx" */
#ifndef LOG_MACROS_H
#define LOG_MACROS_H
/* polyspace + 2 MISRA-CPP:16-0-6 [Justified:Unset] "Comment xxxx" */
#define LOG(level, ...) \
  xxxx::log(xxxx::LogLevel::level, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

#define LOG_VERBOSE(...) LOG(LOG_LEVEL_VERBOSE, __VA_ARGS__)
#define LOG_DEBUG(...) LOG(LOG_LEVEL_DEBUG, __VA_ARGS__)
#define LOG_INFO(...) LOG(LOG_LEVEL_INFO, __VA_ARGS__)
#define LOG_WARN(...) LOG(LOG_LEVEL_WARNING, __VA_ARGS__)
#define LOG_ERROR(...) LOG(LOG_LEVEL_ERROR, __VA_ARGS__)
#define LOG_PANIC(...) \
                            LOG(LOG_LEVEL_FATAL, __VA_ARGS__); \
                            throw std::runtime_error("panic")
#endif // LOG_MACROS_H

namespace xxxx {

enum LogLevel : std::int32_t {
  LOG_LEVEL_FATAL = 0,
  LOG_LEVEL_ERROR = 1,
  LOG_LEVEL_WARNING = 2,
  LOG_LEVEL_INFO = 3,
  LOG_LEVEL_DEBUG = 4,
  LOG_LEVEL_VERBOSE = 5,
  LOG_LEVEL_MAX = 6,
};

void log_print(LogLevel src_lev, const std::string & amp;src_file, const std::string & amp;src_func, int32_t src_line,
                    const std::string & amp;in_buf_str);

template <typename... ARGS_TYPE>
void log(LogLevel src_lev, const std::string & amp;src_file, const std::string & amp;src_func, int32_t src_line,
              const std::string & amp;fmt_str, ARGS_TYPE & amp; & amp;... fmt_args) {
  std::string log_buf(1024, '\0');
  const int log_len = std::snprintf( & amp;log_buf[0], log_buf.size(), fmt_str.c_str(), std::forward<ARGS_TYPE>(fmt_args)...);
  if (log_len > 0) {
    log_print(src_lev, src_file, src_func, src_line, log_buf);
  }
}

} // namespace xxxx

#endif // LOG_H_

log_c++.cpp

#include<mutex>
#include <memory>
#include <iostream>

#include "log_c + + .h"

namespace xxxx {
class LogHelper {
 public:
  template <typename... ARGS_TYPE>
  void get_logger(LogLevel src_lev, const std::string & amp;src_file, const std::string & amp;src_func, int32_t src_line,
              const std::string & amp;fmt_str, ARGS_TYPE & amp; & amp;... fmt_args) {
    //The autosar object is called here to output
std::cout << xxx << std::endl;
  }

  static std::shared_ptr<LogHelper> get_instance() {
    if (initialized_instance_ == nullptr) {
      const std::lock_guard<std::mutex> log_lock(init_mutex_);
      if (initialized_instance_ == nullptr) {
        initialized_instance_ = std::make_shared<LogHelper>();
      }
    }
    return initialized_instance_;
  }

  LogHelper() {
  }

  ~LogHelper() {
  }

 private:
  static std::shared_ptr<LogHelper> initialized_instance_;
  static std::mutex init_mutex_;
  autosar aaa;
};

std::shared_ptr<LogHelper> LogHelper::initialized_instance_ = nullptr;
std::mutex LogHelper::init_mutex_;

void log_print(LogLevel src_lev, const std::string & amp;src_file, const std::string & amp;src_func, int32_t src_line,
                  const std::string & amp;in_buf_str) {
  LogHelper::get_instance()->get_logger(src_lev, src_file, src_func, src_line, in_buf_str);
}


} // namespace xxxx

log_callback.h

#ifndef LOG_CALLBACK_H
#define LOG_CALLBACK_H

/* polyspace + 2 AUTOSAR-CPP14:A16-0-1 [Justified:Unset] "Comment xxxx" */
/* polyspace + 1 MISRA-CPP:16-2-1 [Justified:Unset] "Comment xxxx" */
#ifdef __cplusplus

#include <cstdint>

namespace xxxx {
extern "C" {

//Write a function pointer used by C++ (line 16) -- used to register callback functions for the C++ module
//Write a function pointer used by c (line 23) -- used for c using function pointer
// Convenient calls between modules
using LogCallback = void (*)(const int8_t *const src_file, const int8_t *src_func, int32_t src_line,
                                 int32_t src_lev, const int8_t *log_msg);

#else

#include <stdint.h>

typedef void (*LogCallback)(const int8_t *const src_file, const int8_t *src_func, int32_t src_line, int32_t src_lev,
                                const int8_t *log_msg);
enum LogLevel {
  LOG_LEVEL_FATAL = 0,
  LOG_LEVEL_ERROR = 1,
  LOG_LEVEL_WARNING = 2,
  LOG_LEVEL_INFO = 3,
  LOG_LEVEL_DEBUG = 4,
  LOG_LEVEL_VERBOSE = 5,
  LOG_LEVEL_MAX = 6,
};

// The log_cb_s() here is the callback function registered in c++
#define LOG(level, ...) \
  log_cb_s(__FILE__, __FUNCTION__, __LINE__, level, __VA_ARGS__)

#define LOG_VERBOSE(...) LOG(LOG_LEVEL_VERBOSE, __VA_ARGS__)
#define LOG_DEBUG(...) LOG(LOG_LEVEL_DEBUG, __VA_ARGS__)
#define LOG_INFO(...) LOG(LOG_LEVEL_INFO, __VA_ARGS__)
#define LOG_WARN(...) LOG(LOG_LEVEL_WARNING, __VA_ARGS__)
#define LOG_ERROR(...) LOG(LOG_LEVEL_ERROR, __VA_ARGS__)
#define LOG_PANIC(...) LOG(LOG_LEVEL_FATAL, __VA_ARGS__);

extern LogCallback log_cb_s;
#endif // __cplusplus

//Register callback function
void register_callback(LogCallback log_cb);

/* polyspace + 5 MISRA-CPP:16-2-1 [Justified:Unset] "Comment xxxx" */
#ifdef __cplusplus
} // extern "C"
} // namespace xxxx
#endif // __cplusplus

#endif // LOG_CALLBACK_H

log_callback.c

#include "log_callback.h"
#include <stddef.h>
#include <unistd.h>

LogCallback log_cb_s = NULL;
void register_callback(LogCallback log_cb) {
  log_cb_s = log_cb;
}

Test code:

c_function_test.h

extern "C" {
  void print_test(void);
}

c_function_test.c

#include "log_callback.h"

void print_test(void) {
    const char* log_buf = "fake log";
    LOG_INFO(log_buf);
}

main.cpp

#include <iostream>
#include "log_c + + .h"
#include "log_callback.h"
#include "c_function_test.h"
#include <thread>
#include <csignal>

int32_t main() {
  try{
    LOG_INFO("This is a debug message %d", 123);
    const xxxx::LogCallback log_cb = [](const int8_t* const src_file, const int8_t* const src_func,
                                            int32_t src_line, int32_t src_lev, const int8_t* const src_msg) {
      const std::string log_file_str{reinterpret_cast<const char*>(src_file)};
      const std::string log_func_str{reinterpret_cast<const char*>(src_func)};
      const std::string log_msg_str{reinterpret_cast<const char*>(src_msg)};
      xxxx::log(static_cast<xxxx::LogLevel>(src_lev), log_file_str, log_func_str,
                     src_line, log_msg_str);
    };
    xxxx::register_callback(log_cb);
    LOG_INFO("Fake::register_callback done!");
    std::this_thread::sleep_for(std::chrono::seconds(1));
    print_test();
  } catch (...) {
    std::cerr << "Exception occurred" << std::endl;
    main_res = 1; // Exception occurred
  }

  return main_res; // Single point of exit
}

This article mainly wants to record the idea that c++ and c share a log library. c++ gives c a callback function. This idea can also be used in many places. For example, some programs that use c code for efficient performance need to use C++ code can be implemented by setting up a callback function!