Tutorial: Developing Tranalyzer plugins in C++

This tutorial gives you an introduction into plugin development for T2 in C++.

Why writing a plugin in C++

  • Some people prefer higher level languages
  • Some libraries or tools only work with C++

The one magic command for the busy people

The simplest way to create a C++ plugin is to use the t2plugin script with the -c and --cpp options (123 is the plugin number):

$ t2plugin -c myPluginName -n 123 --cpp
C++ plugin 'myPluginName' created
$

Differences between a C and a C++ plugin

Writing a Tranalyzer plugin in C++ is fundamentally not different from doing it in C. Therefore, this tutorial only highlights the differences or areas where special attention is required. The reader will be referred to the relevant C tutorials for all aspects which do not differ.

Include t2Plugin.hpp instead of t2Plugin.h

The first major difference between a C and a C++ plugin is the name of the file to include in order to have access to T2 functionality:

#include "t2Plugin.hpp"

Prefix all callbacks with T2_API

When implementing a Tranalyzer callback in your C++ file, make sure to prefix it with T2_API:

Note that the T2_PLUGIN_INIT(...) and T2_PLUGIN_INIT_WITH_DEPS(...) macros already take care of the T2_API keyword and can be used as for a C plugin.

Build systems

No matter which build system you use (autotools, cmake or meson), make sure to adapt your filenames, e.g.:

  • t2PSkel.c –> t2PSkel.cpp
  • t2PSkel.h –> t2PSkel.hpp

The changes are suffixed with <----.

Autotools

Edit src/Makefile.am as follows:

  • Replace all references to CFLAGS with CXXFLAGS.
$ cat src/Makefile.am

CMake:

Edit CMakeLists.txt as follows:

  • Change LANGUAGES from C to CXX.
  • Change C_STANDARD to CXX_STANDARD and replace the 99 with the desired C++ standard, e.g., 11.
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)

# If your plugin has only one file named src/pluginName.c,
# then this is the only line you need to change.
set(_plugin_name t2PSkel)
#set(_plugin_number 999)

project(${_plugin_name}
    HOMEPAGE_URL "https://tranalyzer.com"
    VERSION      0.8.10
    LANGUAGES    CXX                        <----
)

# ---------------------------------------------------------------------------- #
# Find libraries                                                               #
# ---------------------------------------------------------------------------- #

#find_package(Threads REQUIRED)
#find_package(Threads QUIET)

#find_package(PkgConfig REQUIRED)
#pkg_check_modules(MY_NAME REQUIRED module_name)

# ---------------------------------------------------------------------------- #
# Source files                                                                 #
# ---------------------------------------------------------------------------- #

add_library(${_plugin_name}
    MODULE
        src/${_plugin_name}.cpp         <----
        #../../utils/t2buf.c
)

# ---------------------------------------------------------------------------- #
# C standard to use                                                            #
# ---------------------------------------------------------------------------- #

set_target_properties(${_plugin_name}
    PROPERTIES
        CXX_STANDARD          11        <----
        #C_EXTENSIONS        ON         <---- you can either comment or remove
        #C_STANDARD_REQUIRED ON         <---- those two lines
)

# ---------------------------------------------------------------------------- #
# Include directories (-I ...)                                                 #
# ---------------------------------------------------------------------------- #

target_include_directories(${_plugin_name}
    PRIVATE
        ../../utils
        ../../tranalyzer2/src
        #../tcpFlags/src        # tell the compiler where to find header
                                # files from dependent plugins
        #${MY_NAME_INCLUDE_DIRS}
)

# ---------------------------------------------------------------------------- #
# Compile options                                                              #
# ---------------------------------------------------------------------------- #

target_compile_options(${_plugin_name}
    PRIVATE
        -Wall
        -Wextra
        -Wundef
)

# ---------------------------------------------------------------------------- #
# Compile definitions (-D ...)                                                 #
# ---------------------------------------------------------------------------- #

#target_compile_definitions(${_plugin_name}
#    PRIVATE
#        PLUGIN_NUMBER=${_plugin_number}
#)

if (APPLE)
    target_compile_definitions(${_plugin_name}
        PRIVATE
            _DARWIN_C_SOURCE
    )
    set_target_properties(${_plugin_name}
        PROPERTIES
            LINK_FLAGS "-undefined dynamic_lookup"
    )
elseif (UNIX)
    target_compile_definitions(${_plugin_name}
        PRIVATE
            _GNU_SOURCE
    )
endif()

# ---------------------------------------------------------------------------- #
# Libraries (-l ..., -L)                                                       #
# ---------------------------------------------------------------------------- #

#target_link_libraries(${_plugin_name}
#    PRIVATE
#        m
#        ${MY_NAME_LIBRARIES}
#        ${MY_NAME_LDFLAGS}
#)

# ---------------------------------------------------------------------------- #
# Installation                                                                 #
# ---------------------------------------------------------------------------- #

set_target_properties(${_plugin_name}
    PROPERTIES
        #PREFIX      ""
        #OUTPUT_NAME "${_plugin_number}_${_plugin_name}"
        OUTPUT_NAME "${_plugin_name}"
        SUFFIX      ".so"
)

Meson

Edit meson.build as follows:

  • Change all language references from c to cpp.
  • Replace c_std=gnu99 with cpp_std=11 (or any desired C++ standard).
$ cat meson.build
project('t2PSkel', 'cpp',               <----
    version: '0.8.10',
    license: 'GPLv2+',
    default_options: [
        'warning_level=2',
        'buildtype=release',
        'cpp_std=c++11'                 <----
    ],
    #meson_version: '>= 0.45.0',
)

plugin_name = meson.project_name()
#plugin_number = '999'

cc = meson.get_compiler('cpp')          <----
os = host_machine.system()
#perl = find_program('perl')

if os == 'darwin'
    add_project_arguments('-D_DARWIN_C_SOURCE', language: 'cpp')    <----
elif os == 'linux'
    add_project_arguments('-D_GNU_SOURCE', language: 'cpp')         <----
#elif os == 'windows'
#    message('You are using Windows...')
#else
#    warning('OS not recognized...')
endif

libm = cc.find_library('m')
threads_dep = dependency('threads')
zlib_dep = dependency('zlib', version: '>=1.2.8', required: false)
if zlib_dep.found()
    message('ZLIB >= 1.2.8 found')
else
    warning('ZLIB >= 1.2.8 not found')
endif

deps = [
    libm,
    threads_dep,
]

inc = include_directories(
    join_paths('..', '..', 'utils'),
    join_paths('..', '..', 'tranalyzer2', 'src'),
    #join_paths('..', 'tcpFlags', 'src'),   # tell the compiler where to find header
                                            # files from dependent plugins
)

#subdir('doc')

src = [
    join_paths('src', plugin_name + '.cpp'),        <----
    #join_paths('..', '..', 'utils', 't2buf.c'),
]

#cmd = run_command(perl, '-nle', 'print $1 if /^#define\s+T2PSKEL_VAR1\s+(\d+).*$/', 'src/' + plugin_name + '.hpp')     <----
#if cmd.returncode() != 0
#    error('Could not determine value of \'T2PSKEL_VAR1\' in \'src/' + plugin_name + '.hpp\'')                          <----
#endif

shared_module(plugin_name,
    sources: src,
    dependencies: deps,
    include_directories: inc,
    #name_prefix: plugin_number + '_',
    name_suffix: 'so',
)

Summary

Implementing a Tranalyzer plugin in C++ does not require much overhead. The main difference is the inclusion of t2Plugin.hpp instead of t2Plugin.h and the T2_API keyword before every Tranalyzer callback functions.

The next tutorial will teach you how to develop a plugin in Rust

See Also