Developing Tranalyzer plugins in C++

plugin development 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

lib_LTLIBRARIES = libt2PSkel.la

libt2PSkel_la_SOURCES = \
	t2PSkel.cpp             # <----
#	../../../utils/t2buf.c  # if you uncomment this line, do not
#	                        # forget to append a backslash above!

libt2PSkel_la_CXXFLAGS = \	                # <----
	-I$(top_srcdir)/../../utils \
	-I$(top_srcdir)/../../tranalyzer2/src
#	-I$(top_srcdir)/../tcpFlags/src         # tell the compiler where to find header
#	                                        # files from dependent plugins
#	                                        # !!! if you uncomment this line, do not
#	                                        # !!! forget to append a backslash above

if APPLE
libt2PSkel_la_CXXFLAGS += -D_DARWIN_C_SOURCE  # macOS specific flags    <----
else
libt2PSkel_la_CXXFLAGS += -D_GNU_SOURCE       #                         <----
endif

libt2PSkel_la_LDFLAGS = -shrext .so  # extension for shared library
                                     # (without this flag, would be '.dylib' on macOS)

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.11
    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.11',
    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