├── .gitignore ├── CMake └── erlang │ ├── CMakeDetermineErlangCompiler.cmake │ ├── CMakeErlangCompiler.cmake.in │ ├── CMakeErlangInformation.cmake │ ├── CMakeTestErlangCompiler.cmake │ └── FindErlang.cmake ├── CMakeLists.txt ├── LICENSE ├── Makefile.am ├── README.markdown ├── assert.cpp ├── assert.hpp ├── autogen.sh ├── config └── .empty ├── configure.ac ├── erlang_functions_hrl.h ├── m4 ├── ax_boost_base.m4 ├── ax_boost_check_header.m4 ├── ax_check_private_header.m4 ├── ax_cxx_check_header.m4 └── ax_erlang_otp_version.m4 ├── main.cpp ├── make.sh ├── make_dev ├── pchar_len_t.h ├── port.cpp ├── port.hpp ├── port_driver.cpp ├── realloc_ptr.hpp ├── test_bindings.erl ├── test_bindings.h ├── test_functions.c └── test_functions.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /CMake/erlang/CMakeDetermineErlangCompiler.cmake: -------------------------------------------------------------------------------- 1 | 2 | # determine the compiler to use for Erlang programs 3 | # NOTE, a generator may set CMAKE_Erlang_COMPILER before 4 | # loading this file to force a compiler. 5 | 6 | IF(NOT CMAKE_Erlang_COMPILER) 7 | # prefer the environment variable CC 8 | IF($ENV{ERLANG_COMPILER} MATCHES ".+") 9 | GET_FILENAME_COMPONENT(CMAKE_Erlang_COMPILER_INIT $ENV{ERLANG_COMPILER} PROGRAM PROGRAM_ARGS CMAKE_Erlang_FLAGS_ENV_INIT) 10 | IF(CMAKE_Erlang_FLAGS_ENV_INIT) 11 | SET(CMAKE_Erlang_COMPILER_ARG1 "${CMAKE_Erlang_FLAGS_ENV_INIT}" CACHE STRING "First argument to Erlang compiler") 12 | ENDIF(CMAKE_Erlang_FLAGS_ENV_INIT) 13 | IF(NOT EXISTS ${CMAKE_Erlang_COMPILER_INIT}) 14 | MESSAGE(SEND_ERROR "Could not find compiler set in environment variable ERLANG_COMPILER:\n$ENV{ERLANG_COMPILER}.") 15 | ENDIF(NOT EXISTS ${CMAKE_Erlang_COMPILER_INIT}) 16 | ENDIF($ENV{ERLANG_COMPILER} MATCHES ".+") 17 | 18 | IF($ENV{ERLANG_RUNTIME} MATCHES ".+") 19 | GET_FILENAME_COMPONENT(CMAKE_Erlang_RUNTIME_INIT $ENV{ERLANG_RUNTIME} PROGRAM PROGRAM_ARGS CMAKE_Erlang_FLAGS_ENV_INIT) 20 | IF(NOT EXISTS ${CMAKE_Erlang_RUNTIME_INIT}) 21 | MESSAGE(SEND_ERROR "Could not find compiler set in environment variable ERLANG_RUNTIME:\n$ENV{ERLANG_RUNTIME}.") 22 | ENDIF(NOT EXISTS ${CMAKE_Erlang_RUNTIME_INIT}) 23 | ENDIF($ENV{ERLANG_RUNTIME} MATCHES ".+") 24 | 25 | IF($ENV{ERLANG_ARCHIVE} MATCHES ".+") 26 | GET_FILENAME_COMPONENT(CMAKE_Erlang_ARCHIVE_INIT $ENV{ERLANG_ARCHIVE} PROGRAM PROGRAM_ARGS CMAKE_Erlang_FLAGS_ENV_INIT) 27 | IF(NOT EXISTS ${CMAKE_Erlang_ARCHIVE_INIT}) 28 | MESSAGE(SEND_ERROR "Could not find compiler set in environment variable ERLANG_ARCHIVE:\n$ENV{ERLANG_ARCHIVE}.") 29 | ENDIF(NOT EXISTS ${CMAKE_Erlang_ARCHIVE_INIT}) 30 | ENDIF($ENV{ERLANG_ARCHIVE} MATCHES ".+") 31 | 32 | SET(Erlang_BIN_PATH 33 | $ENV{ERLANG_HOME}/bin 34 | /usr/bin 35 | /usr/local/bin 36 | /opt/local/bin 37 | /sw/bin 38 | ) 39 | # if no compiler has been specified yet, then look for one 40 | IF(CMAKE_Erlang_COMPILER_INIT) 41 | SET(CMAKE_Erlang_COMPILER ${CMAKE_Erlang_COMPILER_INIT} CACHE PATH "Erlang Compiler") 42 | ELSE(CMAKE_Erlang_COMPILER_INIT) 43 | FIND_PROGRAM(CMAKE_Erlang_COMPILER 44 | NAMES erlc 45 | PATHS ${Erlang_BIN_PATH} 46 | ) 47 | ENDIF(CMAKE_Erlang_COMPILER_INIT) 48 | 49 | # if no runtime has been specified yet, then look for one 50 | IF(CMAKE_Erlang_RUNTIME_INIT) 51 | SET(CMAKE_Erlang_RUNTIME ${CMAKE_Erlang_RUNTIME_INIT} CACHE PATH "Erlang Compiler") 52 | ELSE(CMAKE_Erlang_RUNTIME_INIT) 53 | FIND_PROGRAM(CMAKE_Erlang_RUNTIME 54 | NAMES erl 55 | PATHS ${Erlang_BIN_PATH} 56 | ) 57 | ENDIF(CMAKE_Erlang_RUNTIME_INIT) 58 | 59 | ENDIF(NOT CMAKE_Erlang_COMPILER) 60 | MARK_AS_ADVANCED(CMAKE_Erlang_COMPILER) 61 | 62 | # if no archive has been specified yet, then look for one 63 | IF(CMAKE_Erlang_ARCHIVE_INIT) 64 | SET(CMAKE_Erlang_ARCHIVE ${CMAKE_Erlang_ARCHIVE_INIT} CACHE PATH "Erlang Compiler") 65 | ELSE(CMAKE_Erlang_ARCHIVE_INIT) 66 | FIND_PROGRAM(CMAKE_Erlang_ARCHIVE 67 | NAMES zip 68 | PATHS ${Erlang_BIN_PATH} 69 | ) 70 | ENDIF(CMAKE_Erlang_ARCHIVE_INIT) 71 | MARK_AS_ADVANCED(CMAKE_Erlang_COMPILER) 72 | 73 | # configure variables set in this file for fast reload later on 74 | CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/CMake/erlang/CMakeErlangCompiler.cmake.in 75 | ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeErlangCompiler.cmake IMMEDIATE @ONLY) 76 | SET(CMAKE_Erlang_COMPILER_ENV_VAR "ERLANG_COMPILER") 77 | -------------------------------------------------------------------------------- /CMake/erlang/CMakeErlangCompiler.cmake.in: -------------------------------------------------------------------------------- 1 | SET(CMAKE_Erlang_COMPILER "@CMAKE_Erlang_COMPILER@") 2 | SET(CMAKE_Erlang_COMPILER_ARG1 "@CMAKE_Erlang_COMPILER_ARG1@") 3 | SET(CMAKE_Erlang_RUNTIME "@CMAKE_Erlang_RUNTIME@") 4 | SET(CMAKE_Erlang_ARCHIVE "@CMAKE_Erlang_ARCHIVE@") 5 | SET(CMAKE_Erlang_COMPILER_LOADED 1) 6 | 7 | SET(CMAKE_Erlang_SOURCE_FILE_EXTENSIONS erl) 8 | SET(CMAKE_Erlang_LINKER_PREFERENCE 40) 9 | SET(CMAKE_Erlang_OUTPUT_EXTENSION .beam) 10 | SET(CMAKE_Erlang_OUTPUT_EXTENSION_REPLACE 1) 11 | SET(CMAKE_STATIC_LIBRARY_PREFIX_Erlang "") 12 | SET(CMAKE_STATIC_LIBRARY_SUFFIX_Erlang ".ez") 13 | SET(CMAKE_Erlang_COMPILER_ENV_VAR "ERLANG_COMPILER") 14 | -------------------------------------------------------------------------------- /CMake/erlang/CMakeErlangInformation.cmake: -------------------------------------------------------------------------------- 1 | # This should be included before the _INIT variables are 2 | # used to initialize the cache. Since the rule variables 3 | # have if blocks on them, users can still define them here. 4 | # But, it should still be after the platform file so changes can 5 | # be made to those values. 6 | 7 | IF(CMAKE_USER_MAKE_RULES_OVERRIDE) 8 | INCLUDE(${CMAKE_USER_MAKE_RULES_OVERRIDE}) 9 | ENDIF(CMAKE_USER_MAKE_RULES_OVERRIDE) 10 | 11 | IF(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX) 12 | INCLUDE(${CMAKE_USER_MAKE_RULES_OVERRIDE_CXX}) 13 | ENDIF(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX) 14 | 15 | # this is a place holder if java needed flags for javac they would go here. 16 | IF(NOT CMAKE_Erlang_CREATE_STATIC_LIBRARY) 17 | SET(CMAKE_Erlang_CREATE_STATIC_LIBRARY 18 | " -n app beam ") 19 | ENDIF(NOT CMAKE_Erlang_CREATE_STATIC_LIBRARY) 20 | 21 | # compile a Erlang file into an object file 22 | IF(NOT CMAKE_Erlang_COMPILE_OBJECT) 23 | SET(CMAKE_Erlang_COMPILE_OBJECT 24 | " -o ") 25 | ENDIF(NOT CMAKE_Erlang_COMPILE_OBJECT) 26 | 27 | # compile a Erlang file into an object file 28 | IF(NOT CMAKE_Erlang_LINK_EXECUTABLE) 29 | SET(CMAKE_Erlang_LINK_EXECUTABLE 30 | " -o ") 31 | ENDIF(NOT CMAKE_Erlang_LINK_EXECUTABLE) 32 | 33 | SET(CMAKE_Erlang_FLAGS "-W +debug_info") 34 | -------------------------------------------------------------------------------- /CMake/erlang/CMakeTestErlangCompiler.cmake: -------------------------------------------------------------------------------- 1 | 2 | # This file is used by EnableLanguage in cmGlobalGenerator to 3 | # determine that that selected Erlang compiler can actually compile 4 | # and link the most basic of programs. If not, a fatal error 5 | # is set and cmake stops processing commands and will not generate 6 | # any makefiles or projects. 7 | SET(CMAKE_Erlang_COMPILER_WORKS 1 CACHE INTERNAL "") 8 | -------------------------------------------------------------------------------- /CMake/erlang/FindErlang.cmake: -------------------------------------------------------------------------------- 1 | # - Find Erlang 2 | # This module finds if Erlang is installed and determines where the 3 | # include files and libraries are. This code sets the following 4 | # variables: 5 | # 6 | # ERLANG_RUNTIME = the full path to the Erlang runtime 7 | # ERLANG_COMPILE = the full path to the Erlang compiler 8 | # ERLANG_EI_PATH = the full path to the Erlang erl_interface path 9 | # ERLANG_ERTS_PATH = the full path to the Erlang erts path 10 | # ERLANG_EI_INCLUDE_PATH = /include appended to ERLANG_EI_PATH 11 | # ERLANG_EI_LIBRARY_PATH = /lib appended to ERLANG_EI_PATH 12 | # ERLANG_ERTS_INCLUDE_PATH = /include appended to ERLANG_ERTS_PATH 13 | # ERLANG_ERTS_LIBRARY_PATH = /lib appended to ERLANG_ERTS_PATH 14 | # 15 | 16 | SET(ERLANG_BIN_PATH 17 | $ENV{ERLANG_HOME}/bin 18 | /usr/bin 19 | /usr/local/bin 20 | /opt/local/bin 21 | /sw/bin 22 | ) 23 | FIND_PROGRAM(ERLANG_RUNTIME 24 | NAMES erl 25 | PATHS ${ERLANG_BIN_PATH} 26 | ) 27 | 28 | FIND_PROGRAM(ERLANG_COMPILE 29 | NAMES erlc 30 | PATHS ${ERLANG_BIN_PATH} 31 | ) 32 | 33 | EXECUTE_PROCESS(COMMAND 34 | erl -noshell -eval "io:format(\"~s\", [code:lib_dir()])" -s erlang halt 35 | OUTPUT_VARIABLE ERLANG_OTP_LIB_DIR) 36 | 37 | EXECUTE_PROCESS(COMMAND 38 | erl -noshell -eval "io:format(\"~s\", [code:root_dir()])" -s erlang halt 39 | OUTPUT_VARIABLE ERLANG_OTP_ROOT_DIR) 40 | 41 | MESSAGE(STATUS "Using OTP lib: ${ERLANG_OTP_LIB_DIR} - found") 42 | 43 | EXECUTE_PROCESS(COMMAND ls ${ERLANG_OTP_LIB_DIR} 44 | COMMAND grep erl_interface 45 | COMMAND sort -n 46 | COMMAND tail -1 47 | COMMAND tr -d \n 48 | OUTPUT_VARIABLE ERLANG_EI_DIR) 49 | 50 | EXECUTE_PROCESS(COMMAND ls ${ERLANG_OTP_ROOT_DIR} 51 | COMMAND grep erts 52 | COMMAND sort -n 53 | COMMAND tail -1 54 | COMMAND tr -d \n 55 | OUTPUT_VARIABLE ERLANG_ERTS_DIR) 56 | 57 | MESSAGE(STATUS "Using erl_interface version: ${ERLANG_EI_DIR}") 58 | MESSAGE(STATUS "Using erts version: ${ERLANG_ERTS_DIR}") 59 | 60 | SET(ERLANG_EI_PATH ${ERLANG_OTP_LIB_DIR}/${ERLANG_EI_DIR}) 61 | SET(ERLANG_EI_INCLUDE_PATH ${ERLANG_OTP_LIB_DIR}/${ERLANG_EI_DIR}/include) 62 | SET(ERLANG_EI_LIBRARY_PATH ${ERLANG_OTP_LIB_DIR}/${ERLANG_EI_DIR}/lib) 63 | 64 | SET(ERLANG_ERTS_PATH ${ERLANG_OTP_ROOT_DIR}/${ERLANG_ERTS_DIR}) 65 | SET(ERLANG_ERTS_INCLUDE_PATH ${ERLANG_OTP_ROOT_DIR}/${ERLANG_ERTS_DIR}/include) 66 | SET(ERLANG_ERTS_LIBRARY_PATH ${ERLANG_OTP_ROOT_DIR}/${ERLANG_ERTS_DIR}/lib) 67 | 68 | FIND_PROGRAM(ERLANG_ARCHIVE 69 | NAMES zip 70 | PATHS ${ERLANG_BIN_PATH} 71 | ) 72 | MARK_AS_ADVANCED( 73 | ERLANG_RUNTIME 74 | ERLANG_COMPILE 75 | ERLANG_ARCHIVE 76 | ERLANG_EI_PATH 77 | ERLANG_EI_INCLUDE_PATH 78 | ERLANG_EI_LIBRARY_PATH 79 | ) 80 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | 3 | SET(MODS ${CMAKE_SOURCE_DIR}/CMake/erlang) 4 | SET(CMAKE_MODULE_PATH ${MODS} ${CMAKE_MODULE_PATH}) 5 | SET(CMAKE_PREFIX_PATH /usr/local/ /opt/local/ /usr/ /sw/) 6 | 7 | FIND_PACKAGE(Erlang) 8 | FIND_PACKAGE(Boost) 9 | 10 | PROJECT(GEPD) 11 | 12 | # CHANGE THESE FOR YOUR INDIVIDUAL PROJECT NAMES AND VERSIONS: 13 | SET(target test) 14 | SET(vsn vsn-1) 15 | 16 | SET(PortName ${target}_functions_port_${vsn}) 17 | SET(PortDriverName ${target}_functions_port_driver_${vsn}) 18 | 19 | INCLUDE_DIRECTORIES( 20 | ${ERLANG_EI_INCLUDE_PATH} 21 | ${ERLANG_ERTS_INCLUDE_PATH} 22 | ${Boost_INCLUDE_DIRS} 23 | ) 24 | 25 | LINK_DIRECTORIES( 26 | ${ERLANG_EI_LIBRARY_PATH} 27 | ${Boost_LIBRARY_DIRS} 28 | ) 29 | 30 | SET(inc "-include ${PROJECT_SOURCE_DIR}/${target}_bindings.h") 31 | SET(dver -DCURRENT_VERSION=${vsn}) 32 | SET(CMAKE_CXX_FLAGS "${inc} -save-temps ${dver}") 33 | 34 | ADD_EXECUTABLE(${PortName} port.cpp main.cpp ${target}_functions.c) 35 | TARGET_LINK_LIBRARIES(${PortName} ei ${Boost_LIBRARIES} ) 36 | 37 | ADD_LIBRARY(${PortDriverName} SHARED port_driver.cpp ${target}_functions.c) 38 | SET_TARGET_PROPERTIES(${PortDriverName} PROPERTIES 39 | PREFIX "") 40 | 41 | ADD_DEPENDENCIES(${PortName} ErlangFunctions) 42 | ADD_CUSTOM_TARGET(ErlangFunctions 43 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 44 | COMMAND ${CMAKE_C_COMPILER} -include ${PROJECT_SOURCE_DIR}/${target}_bindings.h ${dver} -I${Boost_INCLUDE_DIRS} -E -P ${PROJECT_SOURCE_DIR}/erlang_functions_hrl.h > erlang_functions.hrl 45 | COMMAND erlc ${PROJECT_SOURCE_DIR}/test_bindings.erl 46 | VERBATIM) 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2009-2023 Michael Truog 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | #-*-Mode:make;coding:utf-8;tab-width:4;c-basic-offset:4-*- 2 | # ex: set ft=make fenc=utf-8 sts=4 ts=4 sw=4 noet nomod: 3 | 4 | INTERFACE_HEADER = erlang_functions.hrl 5 | CURRENT_VERSION = vsn_1 6 | 7 | instdir = $(prefix)/lib/GEPD/ 8 | inst_PROGRAMS = test_functions_port_vsn_1 9 | #inst_LTLIBRARIES = test_functions_port_driver_vsn_1.la 10 | beamdir = "$(instdir)" 11 | beam_DATA = test_bindings.beam 12 | 13 | BUILT_SOURCES = $(INTERFACE_HEADER) $(beam_DATA) 14 | CLEANFILES = $(INTERFACE_HEADER) $(beam_DATA) 15 | 16 | $(INTERFACE_HEADER): Makefile \ 17 | erlang_functions_hrl.h 18 | $(CXX) -DCURRENT_VERSION=$(CURRENT_VERSION) \ 19 | -include $(srcdir)/test_bindings.h \ 20 | $(BOOST_CPPFLAGS) -E -P $(srcdir)/erlang_functions_hrl.h > $@ 21 | 22 | test_functions_port_vsn_1_SOURCES = test_functions.c \ 23 | assert.cpp \ 24 | main.cpp \ 25 | port.cpp 26 | test_functions_port_vsn_1_CPPFLAGS = \ 27 | -I$(ERLANG_LIB_DIR_erl_interface)/include/ \ 28 | -I$(ERLANG_ROOT_DIR)/erts-$(ERLANG_ERTS_VER)/include/ \ 29 | -DCURRENT_VERSION=$(CURRENT_VERSION) $(BOOST_CPPFLAGS) \ 30 | -include $(srcdir)/test_bindings.h $(CXXFLAGS) -save-temps 31 | test_functions_port_vsn_1_LDADD = -lei 32 | test_functions_port_vsn_1_LDFLAGS = -L$(ERLANG_LIB_DIR_erl_interface)/lib/ 33 | 34 | #test_functions_port_driver_vsn_1_la_SOURCES = test_functions.c \ 35 | # port_driver.cpp 36 | #test_functions_port_driver_vsn_1_la_CPPFLAGS = \ 37 | # -fPIC -I$(ERLANG_ROOT_DIR)/erts-$(ERLANG_ERTS_VER)/include/ \ 38 | # -DCURRENT_VERSION=$(CURRENT_VERSION) $(BOOST_CPPFLAGS) \ 39 | # -include $(srcdir)/test_bindings.h $(CXXFLAGS) \ 40 | # -Wno-unused-function -save-temps 41 | #test_functions_port_driver_vsn_1_la_LDFLAGS = -module -avoid-version 42 | 43 | .erl.beam: 44 | @ERLC@ -b beam $(ERLC_OPTS) -o $@ $< 45 | 46 | install-exec-hook: $(beam_DATA) 47 | $(MKDIR_P) $(instdir) 48 | $(INSTALL_DATA) test_bindings.beam $(instdir) 49 | 50 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # GENERIC ERLANG PORT \[DRIVER\] (GEP[D]) 2 | 3 | ## PURPOSE 4 | 5 | To automatically generate an Erlang port driver or Erlang port 6 | for C/C++ bindings using a single self-contained file. 7 | 8 | 9 | Files: 10 | 11 | * `test_bindings.h` contains the ONLY configuration to support the C functions 12 | (in `test_functions.h` and `test_functions.c`) 13 | * `test_bindings.erl` provides the `gen_server` to manage the port or 14 | port driver 15 | * `erlang_functions_hrl.h` creates the erlang function interface 16 | * `port_driver.cpp` provides the Erlang port driver implementation 17 | * `port.cpp` and `port.hpp` provide the Erlang port implementation 18 | 19 | Whether `PORT_DRIVER_NAME_PREFIX` or `PORT_NAME_PREFIX` is defined determines 20 | if a port driver or port is built, respectively (in `test_bindings.h`). 21 | If both are defined, both are built and the port driver is used within 22 | `test_bindings.erl` (undefine `ERL_PORT_DRIVER_NAME` if you want to use 23 | the port). 24 | 25 | 26 | Features: 27 | 28 | * a function binding with only a single line tuple 29 | * efficient passing of function types with no intermediate character buffer 30 | * functionality like the Erlang Driver Tool Kit (EDTK) (no fd handling though) 31 | * macro expansion to avoid code duplication errors 32 | * floating point type handling 33 | * stdout/stderr handling in the generated port code 34 | 35 | 36 | Caveat: 37 | 38 | (This is no longer true for `Erlang >= R15`) 39 | The generated port driver code can not be used for hot code updating 40 | if it performs an asynchronous call because erts will lock the driver 41 | (making it "permanent") with `driver_lock_driver()` 42 | (`http://erlang.org/doc/man/erl_driver.html#driver_lock_driver`). 43 | With the driver locked, there is no possibility that an async 44 | operation would create instability after a hot code update. 45 | 46 | 47 | ## BUILDING 48 | 49 | `Erlang/OTP >= R14A` is required. 50 | Boost is required for the preprocessor macro expansion code 51 | ([http://www.boost.org/](http://www.boost.org/)). 52 | 53 | To build, use: 54 | 55 | ./autogen.sh 56 | ./configure 57 | make 58 | make install 59 | 60 | ## RUNNING 61 | 62 | The `test_bindings` code should generate output similar to: 63 | 64 | $ erl +A 16 65 | 1> test_bindings:start(). 66 | using port driver 67 | {ok,<0.35.0>} 68 | 2> test_bindings:test(). 69 | sync sleep 70 | async sleep 71 | ... 72 | stdout writing before 2 second sleep 73 | ok 74 | 3> stdout writing after 2 second sleep 75 | stderr 76 | line 77 | break(s) 78 | missasync function call returned: {ok,ok} 79 | 3> 80 | 81 | 82 | ## LICENSE 83 | 84 | MIT License 85 | 86 | 87 | ## CONTACT 88 | 89 | Michael Truog 90 | 91 | 92 | ## THANKS 93 | 94 | * Matt Stancliff (GEPD CMake integration) 95 | * Scott Lystig Fritchie (EDTK, i.e., Erlang Driver Tool Kit) 96 | 97 | -------------------------------------------------------------------------------- /assert.cpp: -------------------------------------------------------------------------------- 1 | //-*-Mode:C++;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | // ex: set ft=cpp fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | // 4 | // MIT License 5 | // 6 | // Copyright (c) 2011-2021 Michael Truog 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files (the "Software"), 10 | // to deal in the Software without restriction, including without limitation 11 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | // and/or sell copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | // DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #undef assert 32 | #include "assert.hpp" 33 | 34 | namespace 35 | { 36 | class assert_exception_msg : public std::exception 37 | { 38 | public: 39 | assert_exception_msg(std::string const & message) throw () : 40 | m_message(message) 41 | { 42 | } 43 | virtual ~assert_exception_msg() throw () 44 | { 45 | } 46 | virtual char const * what() const throw () 47 | { 48 | return m_message.c_str(); 49 | } 50 | private: 51 | std::string m_message; 52 | }; 53 | 54 | class assert_exception : public std::exception 55 | { 56 | public: 57 | assert_exception(std::string const & message) throw () : 58 | m_message(message) 59 | { 60 | } 61 | virtual ~assert_exception() throw () 62 | { 63 | } 64 | virtual char const * what() const throw () 65 | { 66 | return m_message.c_str(); 67 | } 68 | private: 69 | std::string m_message; 70 | }; 71 | } 72 | 73 | namespace boost 74 | { 75 | void assertion_failed_msg(char const * expr, 76 | char const * function, 77 | char const * file, 78 | char const * mm, 79 | long line) 80 | { 81 | std::ostringstream stream; 82 | stream << "assert failure: " << expr << ": " << mm; 83 | throw (boost::enable_error_info(assert_exception_msg(stream.str())) << 84 | boost::throw_function(function) << 85 | boost::throw_file(file) << 86 | boost::throw_line(line)); 87 | } 88 | 89 | void assertion_failed(char const * expr, 90 | char const * function, 91 | char const * file, 92 | long line) 93 | { 94 | std::ostringstream stream; 95 | stream << "assert failure: " << expr; 96 | throw (boost::enable_error_info(assert_exception(stream.str())) << 97 | boost::throw_function(function) << 98 | boost::throw_file(file) << 99 | boost::throw_line(line)); 100 | } 101 | } 102 | 103 | -------------------------------------------------------------------------------- /assert.hpp: -------------------------------------------------------------------------------- 1 | //-*-Mode:C++;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | // ex: set ft=cpp fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | // 4 | // MIT License 5 | // 6 | // Copyright (c) 2011-2021 Michael Truog 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files (the "Software"), 10 | // to deal in the Software without restriction, including without limitation 11 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | // and/or sell copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | // DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #ifndef ASSERT_HPP 28 | #define ASSERT_HPP 29 | 30 | #ifdef assert 31 | #error cassert or assert.h should not be included 32 | #endif 33 | 34 | #ifdef NDEBUG 35 | #define BOOST_DISABLE_ASSERTS 36 | #endif // NDEBUG 37 | 38 | #define BOOST_ENABLE_ASSERT_HANDLER 39 | 40 | #include 41 | 42 | namespace boost 43 | { 44 | void assertion_failed(char const * expr, 45 | char const * function, 46 | char const * file, 47 | long line); 48 | } 49 | 50 | #endif // ASSERT_HPP 51 | 52 | #undef assert 53 | #define assert(E) BOOST_ASSERT(E) 54 | 55 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | DIRECTORY=`dirname $0` 3 | autoreconf --force --install -I config -I m4 $DIRECTORY/configure.ac 4 | exit $? 5 | 6 | -------------------------------------------------------------------------------- /config/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okeuday/GEPD/7bbf15683196d29bf2ca26f0ee61e1666ab372cb/config/.empty -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | #-*-Mode:autoconf;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | # ex: set ft=config fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | 4 | AC_INIT([GEPD], [0.9.5], [mjtruog at protonmail dot com], 5 | [GEPD], [https://github.com/okeuday/GEPD]) 6 | 7 | # paths to check for installations that are not standard but typically used 8 | # (for any libraries that don't depend on executables for paths) 9 | PATHS_NONSYSTEM_INC="" 10 | AC_CHECK_FILE("/opt/local/include", 11 | [PATHS_NONSYSTEM_INC="/opt/local/include $PATHS_NONSYSTEM_INC"]) 12 | AC_CHECK_FILE("/usr/local/include", 13 | [PATHS_NONSYSTEM_INC="/usr/local/include $PATHS_NONSYSTEM_INC"]) 14 | PATHS_NONSYSTEM_LIB="" 15 | AC_CHECK_FILE("/opt/local/lib", 16 | [PATHS_NONSYSTEM_LIB="/opt/local/lib $PATHS_NONSYSTEM_LIB"]) 17 | AC_CHECK_FILE("/usr/local/lib", 18 | [PATHS_NONSYSTEM_LIB="/usr/local/lib $PATHS_NONSYSTEM_LIB"]) 19 | 20 | AC_PREREQ([2.59]) 21 | AC_CONFIG_AUX_DIR(config) 22 | AC_CONFIG_HEADERS([config.h]) 23 | AC_CONFIG_MACRO_DIR([m4]) 24 | AM_INIT_AUTOMAKE([1.10 no-define no-dependencies foreign]) 25 | AC_PROG_MKDIR_P 26 | AC_PROG_CC 27 | AC_PROG_CXX 28 | AC_PROG_SED 29 | AC_PROG_LIBTOOL 30 | AM_PROG_CC_C_O 31 | 32 | # C++ requirements 33 | AX_BOOST_BASE([1.40], , 34 | [AC_MSG_ERROR([Boost >= 1.40 not found, see http://boost.org/])]) 35 | AX_BOOST_CHECK_HEADER(boost/preprocessor.hpp, , 36 | [AC_MSG_ERROR([boost::preprocessor not found])], , 37 | $PATHS_NONSYSTEM_INC) 38 | 39 | # erlang checks 40 | AC_ERLANG_NEED_ERLC() 41 | AC_ERLANG_NEED_ERL() 42 | AX_ERLANG_REQUIRE_OTP_VER([R14], , 43 | [AC_MSG_ERROR([Erlang >= R14 not found])]) 44 | AC_ERLANG_SUBST_ERTS_VER 45 | AC_ERLANG_SUBST_ROOT_DIR 46 | AX_ERLANG_SUBST_OTP_VER 47 | AC_ERLANG_SUBST_LIB_DIR 48 | AC_ERLANG_CHECK_LIB([erl_interface]) 49 | AC_PATH_PROG([ESCRIPT],[escript], ,[`AS_DIRNAME([$ERLC])`]) 50 | AC_PATH_PROG([DIALYZER],[dialyzer], ,[`AS_DIRNAME([$ERLC])`]) 51 | AC_SUBST([ERLC_OPTS], 52 | ["-DERLANG_OTP_VERSION_${ERLANG_OTP_VER} -DERLANG_OTP_VERSION_${ERLANG_OTP_VER_MAJOR} -I $abs_top_srcdir/lib +debug_info +strict_validation +warn_bif_clash +warn_deprecated_function +warn_export_all +warn_export_vars +warn_exported_vars +warn_obsolete_guard +warn_shadow_vars +warn_unused_import +warn_unused_function +warn_unused_record +warn_unused_vars"]) 53 | if test "$ERLANG_OTP_VER_MAJOR" -ge 18 ; then 54 | ERL_OPTS="+C multi_time_warp +c true" 55 | else 56 | ERL_OPTS="" 57 | fi 58 | AC_SUBST(ERL_OPTS) 59 | 60 | AC_CONFIG_FILES([Makefile]) 61 | AC_OUTPUT 62 | 63 | -------------------------------------------------------------------------------- /erlang_functions_hrl.h: -------------------------------------------------------------------------------- 1 | //-*-Mode:C++;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | // ex: set ft=cpp fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | ////////////////////////////////////////////////////////////////////////////// 4 | // 5 | // GENERIC ERLANG PORT [DRIVER] 6 | // automatically create Erlang bindings to C++/C that requires an OS process 7 | // 8 | // MIT License 9 | // 10 | // Copyright (c) 2009-2020 Michael Truog 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a 13 | // copy of this software and associated documentation files (the "Software"), 14 | // to deal in the Software without restriction, including without limitation 15 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 | // and/or sell copies of the Software, and to permit persons to whom the 17 | // Software is furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | // DEALINGS IN THE SOFTWARE. 29 | ////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | //#include // broken with boost >= 1.5? 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | // work-around instead of BOOST_PP_TUPLE_TO_SEQ to correctly handle arity 0 47 | #define TUPLE_TO_SEQ_E(r, data, elem) (elem) 48 | #define TUPLE_TO_SEQ(I, T) \ 49 | BOOST_PP_LIST_FOR_EACH(TUPLE_TO_SEQ_E, _, BOOST_PP_TUPLE_TO_LIST(I, T)) 50 | 51 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_char(N) \ 52 | <> 53 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_uchar(N) \ 54 | <> 55 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_bool(N) \ 56 | encode_bool(CREATE_FUNCTION_ARGUMENTS(_, N, _)) 57 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_int8_t(N) \ 58 | <> 59 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_uint8_t(N) \ 60 | <> 61 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_int16_t(N) \ 62 | <> 63 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_uint16_t(N) \ 64 | <> 65 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_int32_t(N) \ 66 | <> 67 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_uint32_t(N) \ 68 | <> 69 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_time_t(N) \ 70 | <> 71 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_int64_t(N) \ 72 | <> 73 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_uint64_t(N) \ 74 | <> 75 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_float(N) \ 76 | <> 77 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_double(N) \ 78 | <> 79 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_pchar_len(N) \ 80 | encode_pchar_len(CREATE_FUNCTION_ARGUMENTS(_, N, _)) 81 | #define ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_puint32_len(N) \ 82 | encode_puint32_len(CREATE_FUNCTION_ARGUMENTS(_, N, _)) 83 | 84 | #if defined(PORT_DRIVER_NAME) 85 | 86 | // 5 tuple elements in the PORT_DRIVER_FUNCTIONS sequence 87 | #define PORT_DRIVER_FUNCTION_ENTRY_LENGTH 5 88 | // specific tuple elements in the PORT_DRIVER_FUNCTIONS sequence 89 | #define PORT_DRIVER_FUNCTION_ENTRY_NAME 0 90 | #define PORT_DRIVER_FUNCTION_ENTRY_ARGC 1 91 | #define PORT_DRIVER_FUNCTION_ENTRY_ARGV 2 92 | #define PORT_DRIVER_FUNCTION_ENTRY_RETURN 3 93 | #define PORT_DRIVER_FUNCTION_ENTRY_ASYNC 4 94 | // macros to access function data in a PORT_DRIVER_FUNCTIONS tuple entry 95 | #define GET_NAME(FUNCTION) \ 96 | BOOST_PP_TUPLE_ELEM(\ 97 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 98 | PORT_DRIVER_FUNCTION_ENTRY_NAME, FUNCTION\ 99 | ) 100 | #define GET_ARGC(FUNCTION) \ 101 | BOOST_PP_TUPLE_ELEM(\ 102 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 103 | PORT_DRIVER_FUNCTION_ENTRY_ARGC, FUNCTION\ 104 | ) 105 | #define GET_ARGV(FUNCTION) \ 106 | BOOST_PP_TUPLE_ELEM(\ 107 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 108 | PORT_DRIVER_FUNCTION_ENTRY_ARGV, FUNCTION\ 109 | ) 110 | #define GET_RETURN(FUNCTION) \ 111 | BOOST_PP_TUPLE_ELEM(\ 112 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 113 | PORT_DRIVER_FUNCTION_ENTRY_RETURN, FUNCTION\ 114 | ) 115 | #define GET_ASYNC(FUNCTION) \ 116 | BOOST_PP_TUPLE_ELEM(\ 117 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 118 | PORT_DRIVER_FUNCTION_ENTRY_ASYNC, FUNCTION\ 119 | ) 120 | 121 | -define(ERL_PORT_DRIVER_NAME, \ 122 | BOOST_PP_STRINGIZE(PORT_DRIVER_NAME)). 123 | -define(ERL_PORT_DRIVER_NAME_PREFIX, \ 124 | BOOST_PP_STRINGIZE(PORT_DRIVER_NAME_PREFIX)). 125 | #define FUNCTIONS_SEQUENCE PORT_DRIVER_FUNCTIONS 126 | #if defined(PORT_NAME) 127 | -define(ERL_PORT_NAME, \ 128 | BOOST_PP_STRINGIZE(PORT_NAME)). 129 | -define(ERL_PORT_NAME_PREFIX, \ 130 | BOOST_PP_STRINGIZE(PORT_NAME_PREFIX)). 131 | #endif 132 | #elif defined(PORT_NAME) 133 | 134 | // 4 tuple elements in the PORT_FUNCTIONS sequence 135 | #define PORT_FUNCTION_ENTRY_LENGTH 4 136 | // specific tuple elements in the PORT_FUNCTIONS sequence 137 | #define PORT_FUNCTION_ENTRY_NAME 0 138 | #define PORT_FUNCTION_ENTRY_ARGC 1 139 | #define PORT_FUNCTION_ENTRY_ARGV 2 140 | #define PORT_FUNCTION_ENTRY_RETURN 3 141 | // macros to access function data in a PORT_FUNCTIONS tuple entry 142 | #define GET_NAME(FUNCTION) \ 143 | BOOST_PP_TUPLE_ELEM(\ 144 | PORT_FUNCTION_ENTRY_LENGTH, \ 145 | PORT_FUNCTION_ENTRY_NAME, FUNCTION\ 146 | ) 147 | #define GET_ARGC(FUNCTION) \ 148 | BOOST_PP_TUPLE_ELEM(\ 149 | PORT_FUNCTION_ENTRY_LENGTH, \ 150 | PORT_FUNCTION_ENTRY_ARGC, FUNCTION\ 151 | ) 152 | #define GET_ARGV(FUNCTION) \ 153 | BOOST_PP_TUPLE_ELEM(\ 154 | PORT_FUNCTION_ENTRY_LENGTH, \ 155 | PORT_FUNCTION_ENTRY_ARGV, FUNCTION\ 156 | ) 157 | #define GET_RETURN(FUNCTION) \ 158 | BOOST_PP_TUPLE_ELEM(\ 159 | PORT_FUNCTION_ENTRY_LENGTH, \ 160 | PORT_FUNCTION_ENTRY_RETURN, FUNCTION\ 161 | ) 162 | #define GET_ASYNC(FUNCTION) 0 163 | 164 | -define(ERL_PORT_NAME, \ 165 | BOOST_PP_STRINGIZE(PORT_NAME)). 166 | -define(ERL_PORT_NAME_PREFIX, \ 167 | BOOST_PP_STRINGIZE(PORT_NAME_PREFIX)). 168 | #define FUNCTIONS_SEQUENCE PORT_FUNCTIONS 169 | #else 170 | #error Neither PORT_DRIVER_NAME nor PORT_NAME defined 171 | #endif 172 | 173 | #define EXPORT_FUNCTION(Z, N, FUNCTIONS) \ 174 | GET_NAME(BOOST_PP_SEQ_ELEM(N, FUNCTIONS))\ 175 | / \ 176 | BOOST_PP_ADD(GET_ARGC(BOOST_PP_SEQ_ELEM(N, FUNCTIONS)), 1) 177 | 178 | #define CREATE_FUNCTION_ARGUMENTS(Z, N, ARGUMENTS) \ 179 | BOOST_PP_CAT(Arg, N) 180 | 181 | #define ENCODE_ARGUMENT_AS_BINARY(Z, N, ARGUMENTS) \ 182 | BOOST_PP_COMMA() \ 183 | BOOST_PP_CAT(\ 184 | ENCODE_ARGUMENT_AS_BINARY_FROM_TYPE_, \ 185 | BOOST_PP_SEQ_ELEM(N, ARGUMENTS) \ 186 | )(N) 187 | 188 | #define CREATE_FUNCTION(I, DATA, FUNCTION) \ 189 | GET_NAME(FUNCTION) \ 190 | BOOST_PP_LPAREN() \ 191 | Process BOOST_PP_COMMA_IF(GET_ARGC(FUNCTION)) \ 192 | BOOST_PP_ENUM( \ 193 | GET_ARGC(FUNCTION), \ 194 | CREATE_FUNCTION_ARGUMENTS, \ 195 | TUPLE_TO_SEQ(GET_ARGC(FUNCTION), GET_ARGV(FUNCTION)) \ 196 | ) \ 197 | BOOST_PP_RPAREN() \ 198 | -> \ 199 | BOOST_PP_IF(\ 200 | GET_ASYNC(FUNCTION), \ 201 | call_port_async, \ 202 | call_port_sync \ 203 | )\ 204 | BOOST_PP_LPAREN() \ 205 | Process, \ 206 | BOOST_PP_DEC(I), \ 207 | [\ 208 | <>\ 209 | BOOST_PP_REPEAT_FROM_TO(\ 210 | 0,\ 211 | GET_ARGC(FUNCTION),\ 212 | ENCODE_ARGUMENT_AS_BINARY,\ 213 | TUPLE_TO_SEQ(GET_ARGC(FUNCTION), GET_ARGV(FUNCTION))\ 214 | )\ 215 | ]\ 216 | BOOST_PP_RPAREN(). 217 | 218 | 219 | -export([ 220 | BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(FUNCTIONS_SEQUENCE), 221 | EXPORT_FUNCTION, FUNCTIONS_SEQUENCE) 222 | ]). 223 | 224 | -vsn(BOOST_PP_STRINGIZE(CURRENT_VERSION)). 225 | 226 | BOOST_PP_SEQ_FOR_EACH(CREATE_FUNCTION, _, FUNCTIONS_SEQUENCE) 227 | 228 | -compile({nowarn_unused_function, 229 | [{encode_bool, 1}, 230 | {encode_pchar_len, 1}, 231 | {encode_puint32_len, 1}]}). 232 | 233 | encode_bool(true) -> 234 | <<1>>; 235 | encode_bool(false) -> 236 | <<0>>. 237 | 238 | encode_pchar_len(Value) when is_binary(Value) -> 239 | DataSize = erlang:byte_size(Value), 240 | <>; 241 | encode_pchar_len(Value) when is_list(Value) -> 242 | ValueList = [<> || E <- Value], 243 | Data = erlang:list_to_binary(ValueList), 244 | DataSize = erlang:byte_size(Data), 245 | <>. 246 | 247 | encode_puint32_len(Value) when is_list(Value) -> 248 | ValueList = [<> || E <- Value], 249 | Data = erlang:list_to_binary(ValueList), 250 | DataSize = erlang:byte_size(Data) div 4, 251 | <>. 252 | 253 | -------------------------------------------------------------------------------- /m4/ax_boost_base.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_boost_base.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Test for the Boost C++ libraries of a particular version (or newer) 12 | # 13 | # If no path to the installed boost library is given the macro searchs 14 | # under /usr, /usr/local, /opt and /opt/local and evaluates the 15 | # $BOOST_ROOT environment variable. Further documentation is available at 16 | # . 17 | # 18 | # This macro calls: 19 | # 20 | # AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) 21 | # 22 | # And sets: 23 | # 24 | # HAVE_BOOST 25 | # 26 | # LICENSE 27 | # 28 | # Copyright (c) 2008 Thomas Porschberg 29 | # Copyright (c) 2009 Peter Adolphs 30 | # 31 | # Copying and distribution of this file, with or without modification, are 32 | # permitted in any medium without royalty provided the copyright notice 33 | # and this notice are preserved. This file is offered as-is, without any 34 | # warranty. 35 | 36 | #serial 49 37 | 38 | # example boost program (need to pass version) 39 | m4_define([_AX_BOOST_BASE_PROGRAM], 40 | [AC_LANG_PROGRAM([[ 41 | #include 42 | ]],[[ 43 | (void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))])); 44 | ]])]) 45 | 46 | AC_DEFUN([AX_BOOST_BASE], 47 | [ 48 | AC_ARG_WITH([boost], 49 | [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], 50 | [use Boost library from a standard location (ARG=yes), 51 | from the specified location (ARG=), 52 | or disable it (ARG=no) 53 | @<:@ARG=yes@:>@ ])], 54 | [ 55 | AS_CASE([$withval], 56 | [no],[want_boost="no";_AX_BOOST_BASE_boost_path=""], 57 | [yes],[want_boost="yes";_AX_BOOST_BASE_boost_path=""], 58 | [want_boost="yes";_AX_BOOST_BASE_boost_path="$withval"]) 59 | ], 60 | [want_boost="yes"]) 61 | 62 | 63 | AC_ARG_WITH([boost-libdir], 64 | [AS_HELP_STRING([--with-boost-libdir=LIB_DIR], 65 | [Force given directory for boost libraries. 66 | Note that this will override library path detection, 67 | so use this parameter only if default library detection fails 68 | and you know exactly where your boost libraries are located.])], 69 | [ 70 | AS_IF([test -d "$withval"], 71 | [_AX_BOOST_BASE_boost_lib_path="$withval"], 72 | [AC_MSG_ERROR([--with-boost-libdir expected directory name])]) 73 | ], 74 | [_AX_BOOST_BASE_boost_lib_path=""]) 75 | 76 | BOOST_LDFLAGS="" 77 | BOOST_CPPFLAGS="" 78 | AS_IF([test "x$want_boost" = "xyes"], 79 | [_AX_BOOST_BASE_RUNDETECT([$1],[$2],[$3])]) 80 | AC_SUBST(BOOST_CPPFLAGS) 81 | AC_SUBST(BOOST_LDFLAGS) 82 | ]) 83 | 84 | 85 | # convert a version string in $2 to numeric and affect to polymorphic var $1 86 | AC_DEFUN([_AX_BOOST_BASE_TONUMERICVERSION],[ 87 | AS_IF([test "x$2" = "x"],[_AX_BOOST_BASE_TONUMERICVERSION_req="1.20.0"],[_AX_BOOST_BASE_TONUMERICVERSION_req="$2"]) 88 | _AX_BOOST_BASE_TONUMERICVERSION_req_shorten=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '\([[0-9]]*\.[[0-9]]*\)'` 89 | _AX_BOOST_BASE_TONUMERICVERSION_req_major=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '\([[0-9]]*\)'` 90 | AS_IF([test "x$_AX_BOOST_BASE_TONUMERICVERSION_req_major" = "x"], 91 | [AC_MSG_ERROR([You should at least specify libboost major version])]) 92 | _AX_BOOST_BASE_TONUMERICVERSION_req_minor=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '[[0-9]]*\.\([[0-9]]*\)'` 93 | AS_IF([test "x$_AX_BOOST_BASE_TONUMERICVERSION_req_minor" = "x"], 94 | [_AX_BOOST_BASE_TONUMERICVERSION_req_minor="0"]) 95 | _AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` 96 | AS_IF([test "X$_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor" = "X"], 97 | [_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor="0"]) 98 | _AX_BOOST_BASE_TONUMERICVERSION_RET=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req_major \* 100000 \+ $_AX_BOOST_BASE_TONUMERICVERSION_req_minor \* 100 \+ $_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor` 99 | AS_VAR_SET($1,$_AX_BOOST_BASE_TONUMERICVERSION_RET) 100 | ]) 101 | 102 | dnl Run the detection of boost should be run only if $want_boost 103 | AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[ 104 | _AX_BOOST_BASE_TONUMERICVERSION(WANT_BOOST_VERSION,[$1]) 105 | succeeded=no 106 | 107 | 108 | AC_REQUIRE([AC_CANONICAL_HOST]) 109 | dnl On 64-bit systems check for system libraries in both lib64 and lib. 110 | dnl The former is specified by FHS, but e.g. Debian does not adhere to 111 | dnl this (as it rises problems for generic multi-arch support). 112 | dnl The last entry in the list is chosen by default when no libraries 113 | dnl are found, e.g. when only header-only libraries are installed! 114 | AS_CASE([${host_cpu}], 115 | [x86_64],[libsubdirs="lib64 libx32 lib lib64"], 116 | [mips*64*],[libsubdirs="lib64 lib32 lib lib64"], 117 | [ppc64|powerpc64|s390x|sparc64|aarch64|ppc64le|powerpc64le|riscv64|e2k],[libsubdirs="lib64 lib lib64"], 118 | [libsubdirs="lib"] 119 | ) 120 | 121 | dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give 122 | dnl them priority over the other paths since, if libs are found there, they 123 | dnl are almost assuredly the ones desired. 124 | AS_CASE([${host_cpu}], 125 | [i?86],[multiarch_libsubdir="lib/i386-${host_os}"], 126 | [armv7l],[multiarch_libsubdir="lib/arm-${host_os}"], 127 | [multiarch_libsubdir="lib/${host_cpu}-${host_os}"] 128 | ) 129 | 130 | dnl first we check the system location for boost libraries 131 | dnl this location ist chosen if boost libraries are installed with the --layout=system option 132 | dnl or if you install boost with RPM 133 | AS_IF([test "x$_AX_BOOST_BASE_boost_path" != "x"],[ 134 | AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION) includes in "$_AX_BOOST_BASE_boost_path/include"]) 135 | AS_IF([test -d "$_AX_BOOST_BASE_boost_path/include" && test -r "$_AX_BOOST_BASE_boost_path/include"],[ 136 | AC_MSG_RESULT([yes]) 137 | BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path/include" 138 | for _AX_BOOST_BASE_boost_path_tmp in $multiarch_libsubdir $libsubdirs; do 139 | AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION) lib path in "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp"]) 140 | AS_IF([test -d "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp" && test -r "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp" ],[ 141 | AC_MSG_RESULT([yes]) 142 | BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp"; 143 | break; 144 | ], 145 | [AC_MSG_RESULT([no])]) 146 | done],[ 147 | AC_MSG_RESULT([no])]) 148 | ],[ 149 | if test X"$cross_compiling" = Xyes; then 150 | search_libsubdirs=$multiarch_libsubdir 151 | else 152 | search_libsubdirs="$multiarch_libsubdir $libsubdirs" 153 | fi 154 | for _AX_BOOST_BASE_boost_path_tmp in /usr /usr/local /opt /opt/local ; do 155 | if test -d "$_AX_BOOST_BASE_boost_path_tmp/include/boost" && test -r "$_AX_BOOST_BASE_boost_path_tmp/include/boost" ; then 156 | for libsubdir in $search_libsubdirs ; do 157 | if ls "$_AX_BOOST_BASE_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi 158 | done 159 | BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path_tmp/$libsubdir" 160 | BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path_tmp/include" 161 | break; 162 | fi 163 | done 164 | ]) 165 | 166 | dnl overwrite ld flags if we have required special directory with 167 | dnl --with-boost-libdir parameter 168 | AS_IF([test "x$_AX_BOOST_BASE_boost_lib_path" != "x"], 169 | [BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_lib_path"]) 170 | 171 | AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION)]) 172 | CPPFLAGS_SAVED="$CPPFLAGS" 173 | CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" 174 | export CPPFLAGS 175 | 176 | LDFLAGS_SAVED="$LDFLAGS" 177 | LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" 178 | export LDFLAGS 179 | 180 | AC_REQUIRE([AC_PROG_CXX]) 181 | AC_LANG_PUSH(C++) 182 | AC_COMPILE_IFELSE([_AX_BOOST_BASE_PROGRAM($WANT_BOOST_VERSION)],[ 183 | AC_MSG_RESULT(yes) 184 | succeeded=yes 185 | found_system=yes 186 | ],[ 187 | ]) 188 | AC_LANG_POP([C++]) 189 | 190 | 191 | 192 | dnl if we found no boost with system layout we search for boost libraries 193 | dnl built and installed without the --layout=system option or for a staged(not installed) version 194 | if test "x$succeeded" != "xyes" ; then 195 | CPPFLAGS="$CPPFLAGS_SAVED" 196 | LDFLAGS="$LDFLAGS_SAVED" 197 | BOOST_CPPFLAGS= 198 | if test -z "$_AX_BOOST_BASE_boost_lib_path" ; then 199 | BOOST_LDFLAGS= 200 | fi 201 | _version=0 202 | if test -n "$_AX_BOOST_BASE_boost_path" ; then 203 | if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path"; then 204 | for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do 205 | _version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` 206 | V_CHECK=`expr $_version_tmp \> $_version` 207 | if test "x$V_CHECK" = "x1" ; then 208 | _version=$_version_tmp 209 | fi 210 | VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` 211 | BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path/include/boost-$VERSION_UNDERSCORE" 212 | done 213 | dnl if nothing found search for layout used in Windows distributions 214 | if test -z "$BOOST_CPPFLAGS"; then 215 | if test -d "$_AX_BOOST_BASE_boost_path/boost" && test -r "$_AX_BOOST_BASE_boost_path/boost"; then 216 | BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path" 217 | fi 218 | fi 219 | dnl if we found something and BOOST_LDFLAGS was unset before 220 | dnl (because "$_AX_BOOST_BASE_boost_lib_path" = ""), set it here. 221 | if test -n "$BOOST_CPPFLAGS" && test -z "$BOOST_LDFLAGS"; then 222 | for libsubdir in $libsubdirs ; do 223 | if ls "$_AX_BOOST_BASE_boost_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi 224 | done 225 | BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path/$libsubdir" 226 | fi 227 | fi 228 | else 229 | if test "x$cross_compiling" != "xyes" ; then 230 | for _AX_BOOST_BASE_boost_path in /usr /usr/local /opt /opt/local ; do 231 | if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path" ; then 232 | for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do 233 | _version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` 234 | V_CHECK=`expr $_version_tmp \> $_version` 235 | if test "x$V_CHECK" = "x1" ; then 236 | _version=$_version_tmp 237 | best_path=$_AX_BOOST_BASE_boost_path 238 | fi 239 | done 240 | fi 241 | done 242 | 243 | VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` 244 | BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" 245 | if test -z "$_AX_BOOST_BASE_boost_lib_path" ; then 246 | for libsubdir in $libsubdirs ; do 247 | if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi 248 | done 249 | BOOST_LDFLAGS="-L$best_path/$libsubdir" 250 | fi 251 | fi 252 | 253 | if test -n "$BOOST_ROOT" ; then 254 | for libsubdir in $libsubdirs ; do 255 | if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi 256 | done 257 | if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then 258 | version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` 259 | stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` 260 | stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` 261 | V_CHECK=`expr $stage_version_shorten \>\= $_version` 262 | if test "x$V_CHECK" = "x1" && test -z "$_AX_BOOST_BASE_boost_lib_path" ; then 263 | AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) 264 | BOOST_CPPFLAGS="-I$BOOST_ROOT" 265 | BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" 266 | fi 267 | fi 268 | fi 269 | fi 270 | 271 | CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" 272 | export CPPFLAGS 273 | LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" 274 | export LDFLAGS 275 | 276 | AC_LANG_PUSH(C++) 277 | AC_COMPILE_IFELSE([_AX_BOOST_BASE_PROGRAM($WANT_BOOST_VERSION)],[ 278 | AC_MSG_RESULT(yes) 279 | succeeded=yes 280 | found_system=yes 281 | ],[ 282 | ]) 283 | AC_LANG_POP([C++]) 284 | fi 285 | 286 | if test "x$succeeded" != "xyes" ; then 287 | if test "x$_version" = "x0" ; then 288 | AC_MSG_NOTICE([[We could not detect the boost libraries (version $1 or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) 289 | else 290 | AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) 291 | fi 292 | # execute ACTION-IF-NOT-FOUND (if present): 293 | ifelse([$3], , :, [$3]) 294 | else 295 | AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) 296 | # execute ACTION-IF-FOUND (if present): 297 | ifelse([$2], , :, [$2]) 298 | fi 299 | 300 | CPPFLAGS="$CPPFLAGS_SAVED" 301 | LDFLAGS="$LDFLAGS_SAVED" 302 | 303 | ]) 304 | -------------------------------------------------------------------------------- /m4/ax_boost_check_header.m4: -------------------------------------------------------------------------------- 1 | #-*-Mode:m4;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | # ex: set ft=m4 fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | # 4 | # SYNOPSIS 5 | # 6 | # AX_BOOST_CHECK_HEADER([HEADER], 7 | # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], 8 | # [INC], [OTHER-PATHS]) 9 | # 10 | # DESCRIPTION 11 | # 12 | # Provide a way of checking for Boost C++ header files to avoid problems with 13 | # AC_CHECK_HEADER using a C compiler and the Boost include path. 14 | # 15 | # MIT License 16 | # 17 | # Copyright (c) 2012-2017 Michael Truog 18 | # 19 | # Permission is hereby granted, free of charge, to any person obtaining a 20 | # copy of this software and associated documentation files (the "Software"), 21 | # to deal in the Software without restriction, including without limitation 22 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 | # and/or sell copies of the Software, and to permit persons to whom the 24 | # Software is furnished to do so, subject to the following conditions: 25 | # 26 | # The above copyright notice and this permission notice shall be included in 27 | # all copies or substantial portions of the Software. 28 | # 29 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | # DEALINGS IN THE SOFTWARE. 36 | # 37 | 38 | AC_DEFUN([AX_BOOST_CHECK_HEADER], 39 | [ 40 | CPPFLAGS_SAVED="$CPPFLAGS" 41 | CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" 42 | export CPPFLAGS 43 | AX_CXX_CHECK_HEADER($1, $2, $3, $4, $5) 44 | CPPFLAGS="$CPPFLAGS_SAVED" 45 | ]) 46 | 47 | -------------------------------------------------------------------------------- /m4/ax_check_private_header.m4: -------------------------------------------------------------------------------- 1 | #-*-Mode:m4;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | # ex: set ft=m4 fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | # 4 | # SYNOPSIS 5 | # 6 | # AX_CHECK_PRIVATE_HEADER([HEADER], 7 | # [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], 8 | # [INC],[OTHER-PATHS]) 9 | # 10 | # DESCRIPTION 11 | # 12 | # Provide a way of checking for C header files that checks additional 13 | # include directories. 14 | # 15 | # MIT License 16 | # 17 | # Copyright (c) 2012-2017 Michael Truog 18 | # 19 | # Permission is hereby granted, free of charge, to any person obtaining a 20 | # copy of this software and associated documentation files (the "Software"), 21 | # to deal in the Software without restriction, including without limitation 22 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 | # and/or sell copies of the Software, and to permit persons to whom the 24 | # Software is furnished to do so, subject to the following conditions: 25 | # 26 | # The above copyright notice and this permission notice shall be included in 27 | # all copies or substantial portions of the Software. 28 | # 29 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | # DEALINGS IN THE SOFTWARE. 36 | # 37 | 38 | AC_DEFUN([AX_CHECK_PRIVATE_HEADER], 39 | [ 40 | cflags_prefix="" 41 | include_path="" 42 | AS_VAR_PUSHDEF([ac_Header], [ac_cv_header_$1]) 43 | AC_CACHE_CHECK([for $1], ac_Header, 44 | [m4_ifval([$4], 45 | [AC_COMPILE_IFELSE([AC_LANG_SOURCE([$4 46 | @%:@include <$1>])], 47 | [AS_VAR_SET(ac_Header, yes)], 48 | [AS_VAR_SET(ac_Header, no) 49 | ac_check_header_save_CFLAGS=$CFLAGS 50 | for path in $5 ; do 51 | cflags_prefix="-I$path" 52 | include_path=$path 53 | CFLAGS="$ac_check_header_save_CFLAGS $cflags_prefix" 54 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([$4 55 | @%:@include <$1>])], 56 | [AS_VAR_SET(ac_Header, yes) 57 | break], 58 | []) 59 | done 60 | CFLAGS=$ac_check_header_save_CFLAGS])], 61 | [AC_PREPROC_IFELSE([AC_LANG_SOURCE([@%:@include <$1>])], 62 | [AS_VAR_SET(ac_Header, yes)], 63 | [AS_VAR_SET(ac_Header, no) 64 | ac_check_header_save_CFLAGS=$CFLAGS 65 | for path in $5 ; do 66 | cflags_prefix="-I$path" 67 | include_path=$path 68 | CFLAGS="$ac_check_header_save_CFLAGS $cflags_prefix" 69 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([@%:@include <$1>])], 70 | [AS_VAR_SET(ac_Header, yes) 71 | break], 72 | []) 73 | done 74 | CFLAGS=$ac_check_header_save_CFLAGS])]) 75 | ]) 76 | AS_IF([test AS_VAR_GET(ac_Header) = yes], 77 | [m4_toupper(AS_TR_SH($1_CFLAGS))=$cflags_prefix 78 | AC_SUBST(m4_toupper(AS_TR_SH($1_CFLAGS))) 79 | dnl only set if searched for 80 | m4_toupper(AS_TR_SH($1_PATH))=$include_path 81 | AC_SUBST(m4_toupper(AS_TR_SH($1_PATH))) 82 | $2], 83 | [$3]) 84 | AS_VAR_POPDEF([ac_Header]) 85 | ]) 86 | 87 | -------------------------------------------------------------------------------- /m4/ax_cxx_check_header.m4: -------------------------------------------------------------------------------- 1 | #-*-Mode:m4;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | # ex: set ft=m4 fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | # 4 | # SYNOPSIS 5 | # 6 | # AX_CXX_CHECK_HEADER([HEADER], 7 | # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], 8 | # [INC], [OTHER-PATHS]) 9 | # 10 | # DESCRIPTION 11 | # 12 | # Provide a way of checking for C++ header files to avoid problems with 13 | # AC_CHECK_HEADER using a C compiler. 14 | # 15 | # MIT License 16 | # 17 | # Copyright (c) 2011-2021 Michael Truog 18 | # 19 | # Permission is hereby granted, free of charge, to any person obtaining a 20 | # copy of this software and associated documentation files (the "Software"), 21 | # to deal in the Software without restriction, including without limitation 22 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 | # and/or sell copies of the Software, and to permit persons to whom the 24 | # Software is furnished to do so, subject to the following conditions: 25 | # 26 | # The above copyright notice and this permission notice shall be included in 27 | # all copies or substantial portions of the Software. 28 | # 29 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | # DEALINGS IN THE SOFTWARE. 36 | # 37 | 38 | AC_DEFUN([AX_CXX_CHECK_HEADER], 39 | [ 40 | AC_LANG_PUSH([C++]) 41 | AX_CHECK_PRIVATE_HEADER($1, $2, $3, $4, $5) 42 | AC_LANG_POP([C++]) 43 | ]) 44 | 45 | -------------------------------------------------------------------------------- /m4/ax_erlang_otp_version.m4: -------------------------------------------------------------------------------- 1 | #-*-Mode:m4;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | # ex: set ft=m4 fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | # 4 | # SYNOPSIS 5 | # 6 | # AX_ERLANG_SUBST_OTP_VER 7 | # AX_ERLANG_REQUIRE_OTP_VER([VER],[ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Based on the implementation of AC_ERLANG_SUBST_ERTS_VER which was 12 | # written by Romain Lenglet. Under GPL like other autoconf macros. 13 | # 14 | # AX_ERLANG_SUBST_OTP_VER sets: 15 | # 16 | # ERLANG_OTP_VER (e.g., "17.1-rc0") 17 | # ERLANG_OTP_VER_MAJOR_MINOR (e.g., "17_1") 18 | # ERLANG_OTP_VER_MAJOR (e.g., "17") 19 | # ERLANG_OTP_VER_MINOR (e.g., "1") 20 | # ERLANG_OTP_VER_PATCH (e.g., "") 21 | # ERLANG_OTP_VER_RELEASE_CANDIDATE (e.g., "0") 22 | # 23 | # For backwards compatability the script also handles pre 17.0 versions: 24 | # ERLANG_OTP_VER (e.g., "R14B01") 25 | # ERLANG_OTP_VER_MAJOR_MINOR (e.g., "14_01") 26 | # ERLANG_OTP_VER_MAJOR (e.g., "14") 27 | # ERLANG_OTP_VER_MINOR (e.g., "01") 28 | # ERLANG_OTP_VER_PATCH (e.g., "") 29 | # ERLANG_OTP_VER_RELEASE_CANDIDATE (e.g., "") 30 | # 31 | # R14A or later is required due to binary module use. 32 | # 33 | # WARNING: After the 17.0 Erlang/OTP release, both 34 | # ERLANG_OTP_VER_MINOR and ERLANG_OTP_VER_RELEASE_CANDIDATE will be 35 | # no longer correct due to the Erlang/OTP team not wanting to 36 | # provide detailed version information. For autoconf, you should 37 | # instead prefer AC_ERLANG_CHECK_LIB and AC_ERLANG_SUBST_ERTS_VER 38 | # to check the version of individual OTP applications. 39 | # An OTP_VERSION file may be present in the installation. 40 | # If it is, it is checked. 41 | # (http://erlang.org/pipermail/erlang-questions/2014-April/078590.html) 42 | 43 | AC_DEFUN([AX_ERLANG_SUBST_OTP_VER], 44 | [ 45 | AC_REQUIRE([AC_ERLANG_NEED_ERLC]) 46 | AC_REQUIRE([AC_ERLANG_NEED_ERL]) 47 | AC_CACHE_CHECK([for the Erlang OTP release], 48 | [ax_cv_erlang_otp_ver], 49 | [AC_LANG_PUSH([Erlang]) 50 | AC_RUN_IFELSE( 51 | [AC_LANG_PROGRAM([], [ 52 | Version = erlang:system_info(otp_release), 53 | file:write_file("conftest.out", Version), 54 | ReturnValue = 0, 55 | halt(ReturnValue)])], 56 | [ax_cv_erlang_otp_ver=`cat conftest.out` 57 | rm -f conftest.out], 58 | [rm -f conftest.out 59 | AC_MSG_FAILURE([test Erlang program execution failed])]) 60 | AC_LANG_POP([Erlang]) 61 | ]) 62 | ax_erlang_otp_ver_major=`expr $ax_cv_erlang_otp_ver : 'R*\([[0-9]]*\)'` 63 | if test "$ax_erlang_otp_ver_major" -ge 17; then 64 | AC_CACHE_CHECK([for the Erlang OTP version], 65 | [ax_cv_erlang_otp_package_ver], 66 | [AC_LANG_PUSH([Erlang]) 67 | AC_RUN_IFELSE( 68 | [AC_LANG_PROGRAM([], [ 69 | Major = erlang:system_info(otp_release), 70 | Version = try erlang:system_info(otp_correction_package) 71 | catch 72 | error:badarg -> 73 | VersionPath = filename:join([[code:root_dir(), 74 | "releases", Major, 75 | "OTP_VERSION"]]), 76 | case file:read_file(VersionPath) of 77 | {ok, FileVersion} -> 78 | %% 17.1.x and after 79 | [[DetailedVersion | 80 | _]] = binary:split(FileVersion, 81 | [[<<"\r">>, <<"\n">>, 82 | <<" ">>]]), 83 | binary_to_list(DetailedVersion); 84 | {error, _} -> 85 | Major ++ ".0.0" 86 | end 87 | end, 88 | file:write_file("conftest.out", Version), 89 | ReturnValue = 0, 90 | halt(ReturnValue)])], 91 | [ax_cv_erlang_otp_package_ver=`cat conftest.out` 92 | rm -f conftest.out], 93 | [rm -f conftest.out 94 | AC_MSG_FAILURE([test Erlang program execution failed])]) 95 | AC_LANG_POP([Erlang]) 96 | ]) 97 | ax_erlang_otp_ver_minor=`expr $ax_cv_erlang_otp_package_ver : '[[0-9]]*\.\([[0-9]]*\)'` 98 | ax_erlang_otp_ver_patch=`expr $ax_cv_erlang_otp_package_ver : '[[0-9]]*\.[[0-9]]*.\([[0-9]]*\)'` 99 | ax_erlang_otp_ver_release_candidate=`expr $ax_cv_erlang_otp_package_ver : '.*-rc\([[0-8]]\)'` 100 | AC_SUBST([ERLANG_OTP_VER], [$ax_cv_erlang_otp_package_ver]) 101 | else 102 | ax_erlang_otp_ver_minor=`expr $ax_cv_erlang_otp_ver : 'R[[0-9]]*[[AB]]\([[0-9]]*\)'` 103 | ax_erlang_otp_ver_patch="" 104 | ax_erlang_otp_ver_type=`expr $ax_cv_erlang_otp_ver : 'R[[0-9]]*\([[AB]]\)'` 105 | if test "$ax_erlang_otp_ver_type" = "A"; then 106 | ax_erlang_otp_ver_release_candidate="0" 107 | elif test "$ax_erlang_otp_ver_type" = "B"; then 108 | ax_erlang_otp_ver_release_candidate="" 109 | else 110 | AC_MSG_FAILURE([invalid version]) 111 | fi 112 | AC_SUBST([ERLANG_OTP_VER], [$ax_cv_erlang_otp_ver]) 113 | fi 114 | AC_SUBST([ERLANG_OTP_VER_MAJOR_MINOR], 115 | ["${ax_erlang_otp_ver_major}_${ax_erlang_otp_ver_minor}"]) 116 | AC_SUBST([ERLANG_OTP_VER_MAJOR], [$ax_erlang_otp_ver_major]) 117 | AC_SUBST([ERLANG_OTP_VER_MINOR], [$ax_erlang_otp_ver_minor]) 118 | AC_SUBST([ERLANG_OTP_VER_PATCH], [$ax_erlang_otp_ver_patch]) 119 | AC_SUBST([ERLANG_OTP_VER_RELEASE_CANDIDATE], 120 | [$ax_erlang_otp_ver_release_candidate]) 121 | ]) 122 | 123 | AC_DEFUN([AX_ERLANG_REQUIRE_OTP_VER], 124 | [ 125 | erlang_otp_version_req=ifelse([$1], ,R10B01,$1) 126 | erlang_otp_version_req_major=`expr $erlang_otp_version_req : 'R*\([[0-9]]*\)'` 127 | if test "$erlang_otp_version_req_major" -ge 17; then 128 | erlang_otp_version_req_minor=`expr $erlang_otp_version_req : '[[0-9]]*\.\([[0-9]]*\)'` 129 | erlang_otp_version_req_patch=`expr $erlang_otp_version_req : '[[0-9]]*\.[[0-9]]*.\([[0-9]]*\)'` 130 | erlang_otp_version_req_release_candidate=`expr $erlang_otp_version_req : '.*-rc\([[0-9]]\)'` 131 | if test "x$erlang_otp_version_req_minor" = "x"; then 132 | erlang_otp_version_req_minor="0" 133 | fi 134 | if test "x$erlang_otp_version_req_patch" = "x"; then 135 | erlang_otp_version_req_patch="0" 136 | fi 137 | if test "x$erlang_otp_version_req_release_candidate" = "x"; then 138 | erlang_otp_version_req_release_candidate="0" 139 | fi 140 | erlang_otp_version_req_int=`expr $erlang_otp_version_req_major \* 1000000 \+ $erlang_otp_version_req_minor \* 1000 \+ $erlang_otp_version_req_patch \* 10 \+ $erlang_otp_version_req_release_candidate` 141 | else 142 | erlang_otp_version_req_minor=`expr $erlang_otp_version_req : 'R[[0-9]]*[[AB]]\([[0-9]]*\)'` 143 | erlang_otp_version_req_type=`expr $erlang_otp_version_req : 'R[[0-9]]*\([[AB]]*\)'` 144 | if test "$erlang_otp_version_req_type" = "B"; then 145 | erlang_otp_version_req_int=`expr $erlang_otp_version_req_major \* 1000000 \+ $erlang_otp_version_req_minor \* 1000 \+ 999` 146 | elif test "$erlang_otp_version_req_type" = "A"; then 147 | erlang_otp_version_req_int=`expr $erlang_otp_version_req_major \* 1000000 \+ $erlang_otp_version_req_minor \* 1000` 148 | elif test "x$erlang_otp_version_req_type" = "x"; then 149 | erlang_otp_version_req_int=`expr $erlang_otp_version_req_major \* 1000000` 150 | else 151 | AC_MSG_FAILURE([invalid version]) 152 | fi 153 | fi 154 | 155 | AC_MSG_CHECKING(for Erlang >= $erlang_otp_version_req) 156 | 157 | AC_REQUIRE([AC_ERLANG_NEED_ERLC]) 158 | AC_REQUIRE([AC_ERLANG_NEED_ERL]) 159 | AC_LANG_PUSH([Erlang]) 160 | AC_RUN_IFELSE( 161 | [AC_LANG_PROGRAM([], [ 162 | Version = case erlang:system_info(otp_release) of 163 | [[\$R, Major1, Major2, T1 | MinorT]] -> 164 | %% before 17.0 165 | Minor = if 166 | MinorT == [[]] -> 167 | 0; 168 | true -> 169 | %% Remove -1 from R16B03-1 170 | list_to_integer(lists:takewhile(fun(C) -> 171 | C /= \$- 172 | end, MinorT)) 173 | end, 174 | if 175 | T1 == \$B -> 176 | list_to_integer([[Major1, Major2]]) * 1000000 + 177 | Minor * 1000 + 999; 178 | T1 == \$A -> 179 | list_to_integer([[Major1, Major2]]) * 1000000 + 180 | Minor * 1000 181 | end; 182 | Major -> 183 | %% 17.0 and after 184 | Package = try erlang:system_info(otp_correction_package) 185 | catch 186 | error:badarg -> 187 | VersionPath = filename:join([[code:root_dir(), 188 | "releases", Major, 189 | "OTP_VERSION"]]), 190 | case file:read_file(VersionPath) of 191 | {ok, FileVersion} -> 192 | %% 17.1.x and after 193 | [[DetailedVersion | 194 | _]] = binary:split(FileVersion, 195 | [[<<"\r">>, <<"\n">>, 196 | <<" ">>]]), 197 | binary_to_list(DetailedVersion); 198 | {error, _} -> 199 | Major ++ ".0.0" 200 | end 201 | end, 202 | [[_, MinorBin | 203 | RCStringBin]] = binary:split(list_to_binary(Package), 204 | [[<<".">>, <<"-">>]], 205 | [[global]]), 206 | RCString = [[binary_to_list(B) || B <- RCStringBin]], 207 | {Patch1, Patch2} = case RCString of 208 | [["rc" ++ RCStr | _]] -> 209 | {"0", RCStr}; 210 | [[PatchStr1, "rc" ++ RCStr | _]] -> 211 | {PatchStr1, RCStr}; 212 | [[PatchStr1, PatchStr2 | _]] -> 213 | {PatchStr1, PatchStr2}; 214 | [[PatchStr1]] -> 215 | {PatchStr1, "0"}; 216 | [[]] -> 217 | {"0", "0"} 218 | end, 219 | list_to_integer(Major) * 1000000 + 220 | list_to_integer(binary_to_list(MinorBin)) * 1000 + 221 | list_to_integer(Patch1) * 10 + 222 | list_to_integer(Patch2) 223 | end, 224 | file:write_file("conftest.out", integer_to_list(Version)), 225 | ReturnValue = 0, 226 | halt(ReturnValue)])], 227 | [ax_erlang_otp_ver=`cat conftest.out` 228 | rm -f conftest.out], 229 | [rm -f conftest.out 230 | AC_MSG_FAILURE([test Erlang program execution failed]) 231 | ]) 232 | AC_LANG_POP([Erlang]) 233 | 234 | if test "$ax_erlang_otp_ver" -ge "$erlang_otp_version_req_int"; then 235 | AC_MSG_RESULT(yes) 236 | # execute ACTION-IF-FOUND (if present): 237 | ifelse([$2], , :, [$2]) 238 | else 239 | AC_MSG_RESULT(no) 240 | # execute ACTION-IF-NOT-FOUND (if present): 241 | ifelse([$3], , :, [$3]) 242 | fi 243 | ]) 244 | 245 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | //-*-Mode:C++;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | // ex: set ft=cpp fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | ////////////////////////////////////////////////////////////////////////////// 4 | // 5 | // GENERIC ERLANG PORT [DRIVER] 6 | // automatically create Erlang bindings to C++/C that requires an OS process 7 | // 8 | // MIT License 9 | // 10 | // Copyright (c) 2009-2017 Michael Truog 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a 13 | // copy of this software and associated documentation files (the "Software"), 14 | // to deal in the Software without restriction, including without limitation 15 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 | // and/or sell copies of the Software, and to permit persons to whom the 17 | // Software is furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | // DEALINGS IN THE SOFTWARE. 29 | ////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include "port.hpp" 32 | 33 | int main() 34 | { 35 | return GEPD::default_main(); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This was an old ad-hoc way to build, consider using cmake or autoconf 4 | 5 | ERLANG_INSTALL=/usr/local 6 | ERTS_PATH=$ERLANG_INSTALL/lib/erlang/erts-5.10.1 7 | ERL_INTERFACE_PATH=$ERLANG_INSTALL/lib/erlang/lib/erl_interface-3.7.11 8 | CURRENT_VERSION=vsn-1 9 | 10 | gcc -Wall -Wextra -Werror -g -O0 -I$ERTS_PATH/include/ \ 11 | -o test_functions.o -c -fpic test_functions.c 12 | g++ -Wall -Wextra -Werror -Wno-unused-function -g -O0 -I$ERTS_PATH/include/ \ 13 | -DCURRENT_VERSION=$CURRENT_VERSION \ 14 | -include test_bindings.h \ 15 | -save-temps -o port_driver.o -c -fpic port_driver.cpp \ 16 | && echo "port_driver.ii contains generated code" 17 | g++ -Wall -Wextra -Werror -g -O0 -I$ERTS_PATH/include/ \ 18 | -o test_functions_port_driver_$CURRENT_VERSION.so \ 19 | -shared port_driver.o test_functions.o 20 | 21 | g++ -Wall -Wextra -Werror -g -O0 -I$ERL_INTERFACE_PATH/include/ \ 22 | -DCURRENT_VERSION=$CURRENT_VERSION \ 23 | -include test_bindings.h \ 24 | -save-temps -o port.o -c -fpic port.cpp \ 25 | && echo "port.ii contains generated code" 26 | g++ -Wall -Wextra -Werror -g -O0 -o main.o -c -fpic main.cpp 27 | g++ -Wall -Wextra -Werror -g -O0 \ 28 | -L$ERL_INTERFACE_PATH/lib/ -o test_functions_port_$CURRENT_VERSION \ 29 | port.o test_functions.o main.o -lei 30 | 31 | gcc -Wall -Wextra -Werror -DCURRENT_VERSION=$CURRENT_VERSION \ 32 | -include test_bindings.h \ 33 | -E -P erlang_functions_hrl.h > erlang_functions.hrl 34 | 35 | erlc test_bindings.erl 36 | 37 | -------------------------------------------------------------------------------- /make_dev: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # flags common for development 4 | make CXXFLAGS="-Wall -Wextra -Werror -O0 -g" $* 5 | exit $? 6 | -------------------------------------------------------------------------------- /pchar_len_t.h: -------------------------------------------------------------------------------- 1 | #ifndef GEPD_PCHAR_LEN_T_H 2 | #define GEPD_PCHAR_LEN_T_H 3 | 4 | #include 5 | 6 | /* Memory Usage Concerns 7 | * 8 | * pchar_len_t return values have free() called on the pointer 9 | * after the contents has been processed when using pchar_len_t. 10 | * To have more explicit control of this, use either 11 | * pchar_len_t_free or pchar_len_t_nofree. 12 | * char * return values do not have free() called on the pointer 13 | * when using pchar. To have more explicit control of this, 14 | * use either pchar_free or pchar_nofree. 15 | * 16 | * This is done to facilitate different usage which is common to both. 17 | * If constant strings (char const *) need to be returned, then the return 18 | * value can be char * which becomes a list of integers in Erlang 19 | * (an Erlang "string"). Any heap allocated memory should be passed within 20 | * a pchar_len_t and should use malloc to allocate the pchar variable. 21 | * The pchar_len_t return value becomes a binary in Erlang. Any usage that 22 | * deviates from these typical scenarios should make sure to use the 23 | * "_free" or "_nofree" macro type declaration suffixes for pchar or pchar_len_t 24 | * (to make the memory usage explicit and avoid memory leaks or segfaults). 25 | * 26 | * GEPD can be easily extended to have separate macro type declarations for 27 | * new/delete if necessary. 28 | */ 29 | typedef struct pchar_len_t { 30 | char * pchar; 31 | uint32_t length; 32 | } pchar_len_t; 33 | 34 | #endif /* GEPD_PCHAR_LEN_T_H */ 35 | -------------------------------------------------------------------------------- /port.cpp: -------------------------------------------------------------------------------- 1 | //-*-Mode:C++;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | // ex: set ft=cpp fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | ////////////////////////////////////////////////////////////////////////////// 4 | // 5 | // GENERIC ERLANG PORT [DRIVER] 6 | // automatically create Erlang bindings to C++/C that requires an OS process 7 | // 8 | // MIT License 9 | // 10 | // Copyright (c) 2009-2023 Michael Truog 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a 13 | // copy of this software and associated documentation files (the "Software"), 14 | // to deal in the Software without restriction, including without limitation 15 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 | // and/or sell copies of the Software, and to permit persons to whom the 17 | // Software is furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | // DEALINGS IN THE SOFTWARE. 29 | ////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | // work-around instead of BOOST_PP_TUPLE_TO_SEQ to correctly handle arity 0 54 | #define TUPLE_TO_SEQ_E(r, data, elem) (elem) 55 | #define TUPLE_TO_SEQ(I, T) \ 56 | BOOST_PP_LIST_FOR_EACH(TUPLE_TO_SEQ_E, _, BOOST_PP_TUPLE_TO_LIST(I, T)) 57 | 58 | #include "port.hpp" 59 | #include "realloc_ptr.hpp" 60 | #include "pchar_len_t.h" 61 | 62 | // erlang:open_port/2 option nouse_stdio 63 | #define PORT_READ_FILE_DESCRIPTOR 3 64 | #define PORT_WRITE_FILE_DESCRIPTOR 4 65 | 66 | // code below depends on these prefix types 67 | #define INPUT_PREFIX_TYPE uint16_t // function identifier 68 | #define OUTPUT_PREFIX_TYPE uint32_t // maximum length 69 | 70 | 71 | #if ! defined(PORT_NAME) 72 | #error Define PORT_NAME within the functions header file to specify the \ 73 | executable name 74 | #endif 75 | #if ! defined(PORT_FUNCTIONS) 76 | #if defined(PORT_DRIVER_FUNCTIONS) 77 | // Using PORT_DRIVER_FUNCTIONS to determine PORT_FUNCTIONS 78 | 79 | #define CREATE_PORT_FUNCTIONS_DEFINITION(S, DATA, ELEMENT) (\ 80 | BOOST_PP_TUPLE_ELEM(5, 0, ELEMENT),\ 81 | BOOST_PP_TUPLE_ELEM(5, 1, ELEMENT),\ 82 | BOOST_PP_TUPLE_ELEM(5, 2, ELEMENT),\ 83 | BOOST_PP_TUPLE_ELEM(5, 3, ELEMENT))\ 84 | 85 | #define PORT_FUNCTIONS \ 86 | BOOST_PP_SEQ_TRANSFORM(CREATE_PORT_FUNCTIONS_DEFINITION, _, \ 87 | PORT_DRIVER_FUNCTIONS) 88 | #else 89 | #error Define PORT_FUNCTIONS within the functions header file to specify \ 90 | the functions and their types 91 | #endif 92 | #endif 93 | 94 | // define the structure of the PORT_FUNCTIONS macro data 95 | // (sequence of tuples) 96 | 97 | // 4 tuple elements in the PORT_FUNCTIONS sequence 98 | #define PORT_FUNCTION_ENTRY_LENGTH 4 99 | // specific tuple elements in the PORT_FUNCTIONS sequence 100 | #define PORT_FUNCTION_ENTRY_NAME 0 101 | #define PORT_FUNCTION_ENTRY_ARGC 1 102 | #define PORT_FUNCTION_ENTRY_ARGV 2 103 | #define PORT_FUNCTION_ENTRY_RETURN 3 104 | 105 | // macros to access function data in a PORT_FUNCTIONS tuple entry 106 | 107 | #define GET_NAME(FUNCTION) \ 108 | BOOST_PP_TUPLE_ELEM(\ 109 | PORT_FUNCTION_ENTRY_LENGTH, \ 110 | PORT_FUNCTION_ENTRY_NAME, FUNCTION\ 111 | ) 112 | #define GET_ARGC(FUNCTION) \ 113 | BOOST_PP_TUPLE_ELEM(\ 114 | PORT_FUNCTION_ENTRY_LENGTH, \ 115 | PORT_FUNCTION_ENTRY_ARGC, FUNCTION\ 116 | ) 117 | #define GET_ARGV(FUNCTION) \ 118 | BOOST_PP_TUPLE_ELEM(\ 119 | PORT_FUNCTION_ENTRY_LENGTH, \ 120 | PORT_FUNCTION_ENTRY_ARGV, FUNCTION\ 121 | ) 122 | #define GET_RETURN(FUNCTION) \ 123 | BOOST_PP_TUPLE_ELEM(\ 124 | PORT_FUNCTION_ENTRY_LENGTH, \ 125 | PORT_FUNCTION_ENTRY_RETURN, FUNCTION\ 126 | ) 127 | #define GET_ASYNC(FUNCTION) 0 128 | 129 | // enforce inherent implementation limits 130 | 131 | #if BOOST_PP_SEQ_SIZE(PORT_FUNCTIONS) > 32767 132 | #error Limited to 32767 port functions (type uint16_t is used for "cmd") 133 | #endif 134 | 135 | #if defined(PORT_C_FUNCTIONS_HEADER_FILE) 136 | extern "C" 137 | { 138 | #include PORT_C_FUNCTIONS_HEADER_FILE 139 | } 140 | #elif defined(PORT_CXX_FUNCTIONS_HEADER_FILE) 141 | #include PORT_CXX_FUNCTIONS_HEADER_FILE 142 | #else 143 | #error Neither PORT_C_FUNCTIONS_HEADER_FILE nor \ 144 | PORT_CXX_FUNCTIONS_HEADER_FILE are defined 145 | #endif 146 | 147 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_void \ 148 | (void) 149 | #define STORE_RETURN_VALUE_TYPE_void(CMD) \ 150 | if (ei_encode_version(buffer.get(), &index)) \ 151 | return GEPD::ExitStatus::ei_encode_error; \ 152 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 153 | return GEPD::ExitStatus::ei_encode_error; \ 154 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 155 | return GEPD::ExitStatus::ei_encode_error; \ 156 | if (ei_encode_atom(buffer.get(), &index, "ok")) \ 157 | return GEPD::ExitStatus::ei_encode_error; 158 | 159 | #define GET_TYPE_SIZE_FROM_TYPE_char(N) \ 160 | sizeof(char) 161 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_char(OFFSET) \ 162 | *((char *) &(buffer[(OFFSET)])) 163 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_char \ 164 | char returnValue = 165 | #define STORE_RETURN_VALUE_TYPE_char(CMD) \ 166 | if (ei_encode_version(buffer.get(), &index)) \ 167 | return GEPD::ExitStatus::ei_encode_error; \ 168 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 169 | return GEPD::ExitStatus::ei_encode_error; \ 170 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 171 | return GEPD::ExitStatus::ei_encode_error; \ 172 | if (ei_encode_long(buffer.get(), &index, returnValue)) \ 173 | return GEPD::ExitStatus::ei_encode_error; 174 | 175 | #define GET_TYPE_SIZE_FROM_TYPE_uchar(N) \ 176 | sizeof(unsigned char) 177 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_uchar(OFFSET) \ 178 | *((unsigned char *) &(buffer[(OFFSET)])) 179 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_uchar \ 180 | unsigned char returnValue = 181 | #define STORE_RETURN_VALUE_TYPE_uchar(CMD) \ 182 | if (ei_encode_version(buffer.get(), &index)) \ 183 | return GEPD::ExitStatus::ei_encode_error; \ 184 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 185 | return GEPD::ExitStatus::ei_encode_error; \ 186 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 187 | return GEPD::ExitStatus::ei_encode_error; \ 188 | if (ei_encode_char(buffer.get(), &index, (char) returnValue)) \ 189 | return GEPD::ExitStatus::ei_encode_error; 190 | 191 | #define GET_TYPE_SIZE_FROM_TYPE_bool(N) \ 192 | sizeof(uint8_t) 193 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_bool(OFFSET) \ 194 | *((uint8_t *) &(buffer[(OFFSET)])) 195 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_bool \ 196 | bool returnValue = 197 | #define STORE_RETURN_VALUE_TYPE_bool(CMD) \ 198 | if (ei_encode_version(buffer.get(), &index)) \ 199 | return GEPD::ExitStatus::ei_encode_error; \ 200 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 201 | return GEPD::ExitStatus::ei_encode_error; \ 202 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 203 | return GEPD::ExitStatus::ei_encode_error; \ 204 | if (ei_encode_boolean(buffer.get(), &index, (int) returnValue)) \ 205 | return GEPD::ExitStatus::ei_encode_error; 206 | 207 | #define GET_TYPE_SIZE_FROM_TYPE_int8_t(N) \ 208 | sizeof(int8_t) 209 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_int8_t(OFFSET) \ 210 | *((int8_t *) &(buffer[(OFFSET)])) 211 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_int8_t \ 212 | int8_t returnValue = 213 | #define STORE_RETURN_VALUE_TYPE_int8_t(CMD) \ 214 | if (ei_encode_version(buffer.get(), &index)) \ 215 | return GEPD::ExitStatus::ei_encode_error; \ 216 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 217 | return GEPD::ExitStatus::ei_encode_error; \ 218 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 219 | return GEPD::ExitStatus::ei_encode_error; \ 220 | if (ei_encode_long(buffer.get(), &index, returnValue)) \ 221 | return GEPD::ExitStatus::ei_encode_error; 222 | 223 | #define GET_TYPE_SIZE_FROM_TYPE_uint8_t(N) \ 224 | sizeof(uint8_t) 225 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_uint8_t(OFFSET) \ 226 | *((uint8_t *) &(buffer[(OFFSET)])) 227 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_uint8_t \ 228 | uint8_t returnValue = 229 | #define STORE_RETURN_VALUE_TYPE_uint8_t(CMD) \ 230 | if (ei_encode_version(buffer.get(), &index)) \ 231 | return GEPD::ExitStatus::ei_encode_error; \ 232 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 233 | return GEPD::ExitStatus::ei_encode_error; \ 234 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 235 | return GEPD::ExitStatus::ei_encode_error; \ 236 | if (ei_encode_ulong(buffer.get(), &index, returnValue)) \ 237 | return GEPD::ExitStatus::ei_encode_error; 238 | 239 | #define GET_TYPE_SIZE_FROM_TYPE_int16_t(N) \ 240 | sizeof(int16_t) 241 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_int16_t(OFFSET) \ 242 | *((int16_t *) &(buffer[(OFFSET)])) 243 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_int16_t \ 244 | int16_t returnValue = 245 | #define STORE_RETURN_VALUE_TYPE_int16_t(CMD) \ 246 | if (ei_encode_version(buffer.get(), &index)) \ 247 | return GEPD::ExitStatus::ei_encode_error; \ 248 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 249 | return GEPD::ExitStatus::ei_encode_error; \ 250 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 251 | return GEPD::ExitStatus::ei_encode_error; \ 252 | if (ei_encode_long(buffer.get(), &index, returnValue)) \ 253 | return GEPD::ExitStatus::ei_encode_error; 254 | 255 | #define GET_TYPE_SIZE_FROM_TYPE_uint16_t(N) \ 256 | sizeof(uint16_t) 257 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_uint16_t(OFFSET) \ 258 | *((uint16_t *) &(buffer[(OFFSET)])) 259 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_uint16_t \ 260 | uint16_t returnValue = 261 | #define STORE_RETURN_VALUE_TYPE_uint16_t(CMD) \ 262 | if (ei_encode_version(buffer.get(), &index)) \ 263 | return GEPD::ExitStatus::ei_encode_error; \ 264 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 265 | return GEPD::ExitStatus::ei_encode_error; \ 266 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 267 | return GEPD::ExitStatus::ei_encode_error; \ 268 | if (ei_encode_ulong(buffer.get(), &index, returnValue)) \ 269 | return GEPD::ExitStatus::ei_encode_error; 270 | 271 | #define GET_TYPE_SIZE_FROM_TYPE_int32_t(N) \ 272 | sizeof(int32_t) 273 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_int32_t(OFFSET) \ 274 | *((int32_t *) &(buffer[(OFFSET)])) 275 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_int32_t \ 276 | int32_t returnValue = 277 | #define STORE_RETURN_VALUE_TYPE_int32_t(CMD) \ 278 | if (ei_encode_version(buffer.get(), &index)) \ 279 | return GEPD::ExitStatus::ei_encode_error; \ 280 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 281 | return GEPD::ExitStatus::ei_encode_error; \ 282 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 283 | return GEPD::ExitStatus::ei_encode_error; \ 284 | if (ei_encode_long(buffer.get(), &index, returnValue)) \ 285 | return GEPD::ExitStatus::ei_encode_error; 286 | 287 | #define GET_TYPE_SIZE_FROM_TYPE_uint32_t(N) \ 288 | sizeof(uint32_t) 289 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_uint32_t(OFFSET) \ 290 | *((uint32_t *) &(buffer[(OFFSET)])) 291 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_uint32_t \ 292 | uint32_t returnValue = 293 | #define STORE_RETURN_VALUE_TYPE_uint32_t(CMD) \ 294 | if (ei_encode_version(buffer.get(), &index)) \ 295 | return GEPD::ExitStatus::ei_encode_error; \ 296 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 297 | return GEPD::ExitStatus::ei_encode_error; \ 298 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 299 | return GEPD::ExitStatus::ei_encode_error; \ 300 | if (ei_encode_ulong(buffer.get(), &index, returnValue)) \ 301 | return GEPD::ExitStatus::ei_encode_error; 302 | 303 | #define GET_TYPE_SIZE_FROM_TYPE_int64_t(N) \ 304 | sizeof(int64_t) 305 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_int64_t(OFFSET) \ 306 | *((int64_t *) &(buffer[(OFFSET)])) 307 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_int64_t \ 308 | int64_t returnValue = 309 | #define STORE_RETURN_VALUE_TYPE_int64_t(CMD) \ 310 | if (ei_encode_version(buffer.get(), &index)) \ 311 | return GEPD::ExitStatus::ei_encode_error; \ 312 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 313 | return GEPD::ExitStatus::ei_encode_error; \ 314 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 315 | return GEPD::ExitStatus::ei_encode_error; \ 316 | if (ei_encode_longlong(buffer.get(), &index, returnValue)) \ 317 | return GEPD::ExitStatus::ei_encode_error; 318 | 319 | #define GET_TYPE_SIZE_FROM_TYPE_uint64_t(N) \ 320 | sizeof(uint64_t) 321 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_uint64_t(OFFSET) \ 322 | *((uint64_t *) &(buffer[(OFFSET)])) 323 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_uint64_t \ 324 | uint64_t returnValue = 325 | #define STORE_RETURN_VALUE_TYPE_uint64_t(CMD) \ 326 | if (ei_encode_version(buffer.get(), &index)) \ 327 | return GEPD::ExitStatus::ei_encode_error; \ 328 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 329 | return GEPD::ExitStatus::ei_encode_error; \ 330 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 331 | return GEPD::ExitStatus::ei_encode_error; \ 332 | if (ei_encode_ulonglong(buffer.get(), &index, returnValue)) \ 333 | return GEPD::ExitStatus::ei_encode_error; 334 | 335 | #define GET_TYPE_SIZE_FROM_TYPE_time_t(N) \ 336 | sizeof(uint64_t) 337 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_time_t(OFFSET) \ 338 | *((uint64_t *) &(buffer[(OFFSET)])) 339 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_time_t \ 340 | uint64_t returnValue = 341 | #define STORE_RETURN_VALUE_TYPE_time_t(CMD) \ 342 | if (ei_encode_version(buffer.get(), &index)) \ 343 | return GEPD::ExitStatus::ei_encode_error; \ 344 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 345 | return GEPD::ExitStatus::ei_encode_error; \ 346 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 347 | return GEPD::ExitStatus::ei_encode_error; \ 348 | if (ei_encode_ulonglong(buffer.get(), &index, returnValue)) \ 349 | return GEPD::ExitStatus::ei_encode_error; 350 | 351 | #define GET_TYPE_SIZE_FROM_TYPE_float(N) \ 352 | sizeof(double) 353 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_float(OFFSET) \ 354 | ((float) *((double *) &(buffer[(OFFSET)]))) 355 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_float \ 356 | double returnValue = 357 | #define STORE_RETURN_VALUE_TYPE_float(CMD) \ 358 | if (ei_encode_version(buffer.get(), &index)) \ 359 | return GEPD::ExitStatus::ei_encode_error; \ 360 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 361 | return GEPD::ExitStatus::ei_encode_error; \ 362 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 363 | return GEPD::ExitStatus::ei_encode_error; \ 364 | if (ei_encode_double(buffer.get(), &index, returnValue)) \ 365 | return GEPD::ExitStatus::ei_encode_error; 366 | 367 | #define GET_TYPE_SIZE_FROM_TYPE_double(N) \ 368 | sizeof(double) 369 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_double(OFFSET) \ 370 | *((double *) &(buffer[(OFFSET)])) 371 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_double \ 372 | double returnValue = 373 | #define STORE_RETURN_VALUE_TYPE_double(CMD) \ 374 | if (ei_encode_version(buffer.get(), &index)) \ 375 | return GEPD::ExitStatus::ei_encode_error; \ 376 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 377 | return GEPD::ExitStatus::ei_encode_error; \ 378 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 379 | return GEPD::ExitStatus::ei_encode_error; \ 380 | if (ei_encode_double(buffer.get(), &index, returnValue)) \ 381 | return GEPD::ExitStatus::ei_encode_error; 382 | 383 | #define GET_TYPE_SIZE_FROM_TYPE_pchar_len(N) \ 384 | sizeof(uint32_t) + *((uint32_t *) &(buffer[( \ 385 | BOOST_PP_CAT(offset_arg, N) \ 386 | )])) 387 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_pchar_len(OFFSET) \ 388 | ((char *) &(buffer[(OFFSET + sizeof(uint32_t))])), \ 389 | *((uint32_t *) &(buffer[(OFFSET)])) 390 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t_nofree \ 391 | pchar_len_t returnValue = 392 | #define STORE_RETURN_VALUE_TYPE_pchar_len_t_nofree(CMD) \ 393 | if (ei_encode_version(buffer.get(), &index)) \ 394 | return GEPD::ExitStatus::ei_encode_error; \ 395 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 396 | return GEPD::ExitStatus::ei_encode_error; \ 397 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 398 | return GEPD::ExitStatus::ei_encode_error; \ 399 | if (buffer.reserve(index + returnValue.length) == false) \ 400 | return GEPD::ExitStatus::write_overflow; \ 401 | if (ei_encode_binary(buffer.get(), &index, \ 402 | returnValue.pchar, returnValue.length)) \ 403 | return GEPD::ExitStatus::ei_encode_error; 404 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t_free \ 405 | CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t_nofree 406 | #define STORE_RETURN_VALUE_TYPE_pchar_len_t_free(CMD) \ 407 | STORE_RETURN_VALUE_TYPE_pchar_len_t_nofree(CMD) \ 408 | free(returnValue.pchar); 409 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t \ 410 | CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t_free 411 | #define STORE_RETURN_VALUE_TYPE_pchar_len_t(CMD) \ 412 | STORE_RETURN_VALUE_TYPE_pchar_len_t_free(CMD) 413 | 414 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_nofree \ 415 | char const * returnValue = 416 | #define STORE_RETURN_VALUE_TYPE_pchar_nofree(CMD) \ 417 | if (ei_encode_version(buffer.get(), &index)) \ 418 | return GEPD::ExitStatus::ei_encode_error; \ 419 | if (ei_encode_tuple_header(buffer.get(), &index, 2)) \ 420 | return GEPD::ExitStatus::ei_encode_error; \ 421 | if (ei_encode_ulong(buffer.get(), &index, CMD)) \ 422 | return GEPD::ExitStatus::ei_encode_error; \ 423 | if (buffer.reserve(index + strlen(returnValue) + 1) == false) \ 424 | return GEPD::ExitStatus::write_overflow; \ 425 | if (ei_encode_string(buffer.get(), &index, returnValue)) \ 426 | return GEPD::ExitStatus::ei_encode_error; 427 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_free \ 428 | CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_nofree 429 | #define STORE_RETURN_VALUE_TYPE_pchar_free(CMD) \ 430 | STORE_RETURN_VALUE_TYPE_pchar_nofree(CMD) \ 431 | free(returnValue); 432 | #define CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar \ 433 | CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_nofree 434 | #define STORE_RETURN_VALUE_TYPE_pchar(CMD) \ 435 | STORE_RETURN_VALUE_TYPE_pchar_nofree(CMD) 436 | 437 | #define GET_TYPE_SIZE_FROM_TYPE_puint32_len(N) \ 438 | sizeof(uint32_t) + *((uint32_t *) &(buffer[( \ 439 | BOOST_PP_CAT(offset_arg, N) \ 440 | )])) * sizeof(uint32_t) 441 | #define GET_FUNCTION_ARGUMENT_FROM_TYPE_puint32_len(OFFSET) \ 442 | ((uint32_t *) &(buffer[(OFFSET + sizeof(uint32_t))])), \ 443 | *((uint32_t *) &(buffer[(OFFSET)])) 444 | 445 | namespace 446 | { 447 | // list of non-fatal errors that can be sent back 448 | 449 | // port will send an error for protocol problems 450 | namespace Error 451 | { 452 | char const * const invalid_function = "Invalid function call"; 453 | } 454 | 455 | int errno_read() 456 | { 457 | switch (errno) 458 | { 459 | case EAGAIN: 460 | return GEPD::ExitStatus::read_EAGAIN; 461 | case EBADF: 462 | return GEPD::ExitStatus::read_EBADF; 463 | case EFAULT: 464 | return GEPD::ExitStatus::read_EFAULT; 465 | case EINTR: 466 | return GEPD::ExitStatus::read_EINTR; 467 | case EINVAL: 468 | return GEPD::ExitStatus::read_EINVAL; 469 | case EIO: 470 | return GEPD::ExitStatus::read_EIO; 471 | case EISDIR: 472 | return GEPD::ExitStatus::read_EISDIR; 473 | default: 474 | return GEPD::ExitStatus::read_unknown; 475 | } 476 | } 477 | 478 | int errno_write() 479 | { 480 | switch (errno) 481 | { 482 | case EAGAIN: 483 | return GEPD::ExitStatus::write_EAGAIN; 484 | case EBADF: 485 | return GEPD::ExitStatus::write_EBADF; 486 | case EFAULT: 487 | return GEPD::ExitStatus::write_EFAULT; 488 | case EFBIG: 489 | return GEPD::ExitStatus::write_EFBIG; 490 | case EINTR: 491 | return GEPD::ExitStatus::write_EINTR; 492 | case EINVAL: 493 | return GEPD::ExitStatus::write_EINVAL; 494 | case EIO: 495 | return GEPD::ExitStatus::write_EIO; 496 | case ENOSPC: 497 | return GEPD::ExitStatus::write_ENOSPC; 498 | case EPIPE: 499 | return GEPD::ExitStatus::write_EPIPE; 500 | default: 501 | return GEPD::ExitStatus::write_unknown; 502 | } 503 | } 504 | 505 | int errno_poll() 506 | { 507 | switch (errno) 508 | { 509 | case EBADF: 510 | return GEPD::ExitStatus::poll_EBADF; 511 | case EFAULT: 512 | return GEPD::ExitStatus::poll_EFAULT; 513 | case EINTR: 514 | return GEPD::ExitStatus::poll_EINTR; 515 | case EINVAL: 516 | return GEPD::ExitStatus::poll_EINVAL; 517 | case ENOMEM: 518 | return GEPD::ExitStatus::poll_ENOMEM; 519 | default: 520 | return GEPD::ExitStatus::poll_unknown; 521 | } 522 | } 523 | 524 | int errno_pipe() 525 | { 526 | switch (errno) 527 | { 528 | case EFAULT: 529 | return GEPD::ExitStatus::pipe_EFAULT; 530 | case EINVAL: 531 | return GEPD::ExitStatus::pipe_EINVAL; 532 | case EMFILE: 533 | return GEPD::ExitStatus::pipe_EMFILE; 534 | case ENFILE: 535 | return GEPD::ExitStatus::pipe_ENFILE; 536 | default: 537 | return GEPD::ExitStatus::pipe_unknown; 538 | } 539 | } 540 | 541 | int errno_dup() 542 | { 543 | switch (errno) 544 | { 545 | case EBADF: 546 | return GEPD::ExitStatus::dup_EBADF; 547 | case EBUSY: 548 | return GEPD::ExitStatus::dup_EBUSY; 549 | case EINTR: 550 | return GEPD::ExitStatus::dup_EINTR; 551 | case EINVAL: 552 | return GEPD::ExitStatus::dup_EINVAL; 553 | case EMFILE: 554 | return GEPD::ExitStatus::dup_EMFILE; 555 | default: 556 | return GEPD::ExitStatus::dup_unknown; 557 | } 558 | } 559 | 560 | int errno_close() 561 | { 562 | switch (errno) 563 | { 564 | case EBADF: 565 | return GEPD::ExitStatus::close_EBADF; 566 | case EINTR: 567 | return GEPD::ExitStatus::close_EINTR; 568 | case EIO: 569 | return GEPD::ExitStatus::close_EIO; 570 | default: 571 | return GEPD::ExitStatus::close_unknown; 572 | } 573 | } 574 | 575 | int read_exact(unsigned char * const buffer, 576 | uint32_t const length) 577 | { 578 | uint32_t total = 0; 579 | while (total < length) 580 | { 581 | ssize_t const i = read(PORT_READ_FILE_DESCRIPTOR, 582 | buffer + total, length - total); 583 | if (i <= 0) 584 | { 585 | if (i == -1) 586 | return errno_read(); 587 | else 588 | return GEPD::ExitStatus::read_null; 589 | } 590 | total += i; 591 | } 592 | if (total > length) 593 | return GEPD::ExitStatus::read_overflow; 594 | return GEPD::ExitStatus::success; 595 | } 596 | 597 | int write_exact(unsigned char const * const buffer, 598 | uint32_t const length) 599 | { 600 | uint32_t total = 0; 601 | while (total < length) 602 | { 603 | ssize_t const i = write(PORT_WRITE_FILE_DESCRIPTOR, 604 | buffer + total, length - total); 605 | if (i <= 0) 606 | { 607 | if (i == -1) 608 | return errno_write(); 609 | else 610 | return GEPD::ExitStatus::write_null; 611 | } 612 | total += i; 613 | } 614 | if (total > length) 615 | return GEPD::ExitStatus::write_overflow; 616 | return GEPD::ExitStatus::success; 617 | } 618 | 619 | int read_cmd(realloc_ptr & buffer) 620 | { 621 | unsigned char lengthData[4]; 622 | int const status = read_exact(lengthData, 4); 623 | if (status) 624 | return status; 625 | uint32_t const length = (lengthData[0] << 24) | 626 | (lengthData[1] << 16) | 627 | (lengthData[2] << 8) | 628 | lengthData[3]; 629 | buffer.reserve(length); 630 | return read_exact(buffer.get(), length); 631 | } 632 | 633 | int write_cmd(realloc_ptr & buffer, uint32_t length) 634 | { 635 | buffer[0] = (length & 0xff000000) >> 24; 636 | buffer[1] = (length & 0x00ff0000) >> 16; 637 | buffer[2] = (length & 0x0000ff00) >> 8; 638 | buffer[3] = length & 0x000000ff; 639 | return write_exact(buffer.get(), length + 4); 640 | } 641 | 642 | int reply_error_string(realloc_ptr & buffer, 643 | int & index, uint16_t cmd, char const * const str) 644 | { 645 | if (ei_encode_version(buffer.get(), &index)) 646 | return GEPD::ExitStatus::ei_encode_error; 647 | if (ei_encode_tuple_header(buffer.get(), &index, 3)) 648 | return GEPD::ExitStatus::ei_encode_error; 649 | if (ei_encode_atom(buffer.get(), &index, "error")) 650 | return GEPD::ExitStatus::ei_encode_error; 651 | if (ei_encode_ulong(buffer.get(), &index, cmd)) 652 | return GEPD::ExitStatus::ei_encode_error; 653 | if (buffer.reserve(index + strlen(str) + 1) == false) 654 | return GEPD::ExitStatus::write_overflow; 655 | if (ei_encode_string(buffer.get(), &index, str)) 656 | return GEPD::ExitStatus::ei_encode_error; 657 | return GEPD::ExitStatus::success; 658 | } 659 | 660 | #define STORE_RETURN_VALUE(TYPE, CMD) \ 661 | BOOST_PP_CAT(STORE_RETURN_VALUE_TYPE_, TYPE)(CMD) 662 | 663 | #define GET_FUNCTION_ARGUMENT(TYPE, OFFSET) \ 664 | BOOST_PP_CAT(\ 665 | GET_FUNCTION_ARGUMENT_FROM_TYPE_, TYPE \ 666 | )(OFFSET) 667 | 668 | #define CREATE_FUNCTION_RETURN_VALUE_STORE(TYPE) \ 669 | BOOST_PP_CAT(\ 670 | CREATE_FUNCTION_RETURN_VALUE_STORE_TYPE_, TYPE\ 671 | ) 672 | 673 | #define GET_TYPE_SIZE(TYPE, N) \ 674 | BOOST_PP_CAT(GET_TYPE_SIZE_FROM_TYPE_, TYPE)(N) 675 | 676 | #define STORE_FUNCTION_ARGUMENT_SIZE(TYPE, N, OFFSET) \ 677 | size_t const BOOST_PP_CAT(offset_arg, N) = OFFSET ; 678 | 679 | #define STORE_FUNCTION_ARGUMENTS(Z, N, DATA) \ 680 | STORE_FUNCTION_ARGUMENT_SIZE( \ 681 | BOOST_PP_SEQ_ELEM(N, BOOST_PP_SEQ_ELEM(0, DATA)), \ 682 | BOOST_PP_INC(N), \ 683 | BOOST_PP_CAT(offset_arg, N) \ 684 | + GET_TYPE_SIZE(BOOST_PP_SEQ_ELEM(N, BOOST_PP_SEQ_ELEM(0, DATA)), N) \ 685 | ) 686 | 687 | #define CREATE_FUNCTION_ARGUMENTS(Z, N, DATA) \ 688 | GET_FUNCTION_ARGUMENT(\ 689 | BOOST_PP_SEQ_ELEM(N, BOOST_PP_SEQ_ELEM(0, DATA)), \ 690 | BOOST_PP_CAT(offset_arg, N) \ 691 | ) 692 | 693 | #define CREATE_FUNCTION(I, OFFSETS, FUNCTION) \ 694 | case BOOST_PP_DEC(I):\ 695 | {\ 696 | BOOST_PP_IF(\ 697 | GET_ARGC(FUNCTION),\ 698 | size_t const offset_arg0 = BOOST_PP_SEQ_ELEM(0, OFFSETS);, \ 699 | BOOST_PP_EMPTY() \ 700 | )\ 701 | BOOST_PP_REPEAT_FROM_TO( \ 702 | 0, \ 703 | BOOST_PP_DEC(GET_ARGC(FUNCTION)), \ 704 | STORE_FUNCTION_ARGUMENTS, \ 705 | (TUPLE_TO_SEQ(GET_ARGC(FUNCTION), GET_ARGV(FUNCTION)))\ 706 | (BOOST_PP_SEQ_ELEM(0, OFFSETS)) \ 707 | ) \ 708 | CREATE_FUNCTION_RETURN_VALUE_STORE(GET_RETURN(FUNCTION)) \ 709 | GET_NAME(FUNCTION) \ 710 | BOOST_PP_LPAREN() \ 711 | BOOST_PP_ENUM( \ 712 | GET_ARGC(FUNCTION), \ 713 | CREATE_FUNCTION_ARGUMENTS, \ 714 | (TUPLE_TO_SEQ(GET_ARGC(FUNCTION), GET_ARGV(FUNCTION)))\ 715 | (BOOST_PP_SEQ_ELEM(0, OFFSETS)) \ 716 | ) \ 717 | BOOST_PP_RPAREN() \ 718 | ; \ 719 | int index = BOOST_PP_SEQ_ELEM(1, OFFSETS); \ 720 | STORE_RETURN_VALUE(GET_RETURN(FUNCTION), BOOST_PP_DEC(I)) \ 721 | if ((status = write_cmd(buffer, index - BOOST_PP_SEQ_ELEM(1, OFFSETS)))) \ 722 | return status; \ 723 | return GEPD::ExitStatus::success;\ 724 | } 725 | 726 | int consume_erlang(short & revents, realloc_ptr & buffer) 727 | { 728 | if (revents & POLLERR) 729 | return GEPD::ExitStatus::poll_ERR; 730 | else if (revents & POLLHUP) 731 | return GEPD::ExitStatus::erlang_exit; 732 | else if (revents & POLLNVAL) 733 | return GEPD::ExitStatus::poll_NVAL; 734 | revents = 0; 735 | int status; 736 | if ((status = read_cmd(buffer))) 737 | { 738 | return status; 739 | } 740 | else 741 | { 742 | INPUT_PREFIX_TYPE cmd = *((INPUT_PREFIX_TYPE *) buffer.get()); 743 | switch (cmd) 744 | { 745 | BOOST_PP_SEQ_FOR_EACH(CREATE_FUNCTION, 746 | (sizeof(INPUT_PREFIX_TYPE)) 747 | (sizeof(OUTPUT_PREFIX_TYPE)), 748 | PORT_FUNCTIONS) 749 | 750 | default: 751 | int index = sizeof(OUTPUT_PREFIX_TYPE); 752 | if ((status = reply_error_string(buffer, index, cmd, 753 | Error::invalid_function))) 754 | return status; 755 | if ((status = write_cmd(buffer, index - 756 | sizeof(OUTPUT_PREFIX_TYPE)))) 757 | return status; 758 | return GEPD::ExitStatus::success; 759 | } 760 | } 761 | } 762 | 763 | int store_standard_fd(int in, int & out) 764 | { 765 | int fds[2] = {-1, -1}; 766 | if (pipe(fds) == -1) 767 | return errno_pipe(); 768 | if (dup2(fds[1], in) == -1) 769 | return errno_dup(); 770 | if (close(fds[1]) == -1) 771 | return errno_close(); 772 | out = fds[0]; 773 | return GEPD::ExitStatus::success; 774 | } 775 | 776 | int data_ready(int fd, bool & ready) 777 | { 778 | struct pollfd fds[1] = {{fd, POLLIN | POLLPRI, 0}}; 779 | int const count = poll(fds, 1, 0); 780 | if (count == -1) 781 | return errno_poll(); 782 | ready = (count == 1); 783 | return GEPD::ExitStatus::success; 784 | } 785 | 786 | bool orphaned(unsigned long const ppid) 787 | { 788 | unsigned long const ppid_now = getppid(); 789 | return (ppid_now != ppid); 790 | } 791 | 792 | enum 793 | { 794 | INDEX_STDOUT = 0, 795 | INDEX_STDERR, 796 | INDEX_ERLANG 797 | }; 798 | } 799 | 800 | int GEPD::consume_stream(int fd, short & revents, 801 | char const * const name, unsigned long const pid, 802 | realloc_ptr & send_buffer, 803 | realloc_ptr & stream, size_t & i) 804 | { 805 | if (revents & POLLERR) 806 | return GEPD::ExitStatus::poll_ERR; 807 | else if (revents & POLLHUP) 808 | return GEPD::ExitStatus::poll_HUP; 809 | else if (revents & POLLNVAL) 810 | return GEPD::ExitStatus::poll_NVAL; 811 | revents = 0; 812 | 813 | bool flush = false; 814 | size_t i_flush = 0; 815 | ssize_t left = stream.size() - i; 816 | if (left == 0) 817 | { 818 | // stream is full, flush it 819 | flush = true; 820 | i_flush = i - 1; 821 | } 822 | else 823 | { 824 | ssize_t read_bytes; 825 | while ((read_bytes = read(fd, &stream[i], left)) == left && 826 | stream.grow()) 827 | { 828 | i += left; 829 | left = stream.size() - i; 830 | bool ready = true; 831 | data_ready(fd, ready); 832 | if (ready == false) 833 | break; 834 | } 835 | if (read_bytes == 0 && i == 0) 836 | return GEPD::ExitStatus::success; 837 | else if (read_bytes == -1) 838 | return errno_read(); 839 | i += read_bytes; // i is the next index to read at, always 840 | 841 | // send stdout/stderr output before the last newline character 842 | for (ssize_t j = i - 1; ! flush && j >= 0; --j) 843 | { 844 | if (stream[j] == '\n') 845 | { 846 | flush = true; 847 | i_flush = j; 848 | } 849 | } 850 | } 851 | 852 | if (flush) 853 | { 854 | int index = sizeof(OUTPUT_PREFIX_TYPE); 855 | if (ei_encode_version(send_buffer.get(), &index)) 856 | return GEPD::ExitStatus::ei_encode_error; 857 | if (ei_encode_tuple_header(send_buffer.get(), &index, 3)) 858 | return GEPD::ExitStatus::ei_encode_error; 859 | if (ei_encode_atom(send_buffer.get(), &index, name)) 860 | return GEPD::ExitStatus::ei_encode_error; 861 | if (ei_encode_ulong(send_buffer.get(), &index, pid)) 862 | return GEPD::ExitStatus::ei_encode_error; 863 | if (send_buffer.reserve(index + (i_flush + 1) + 1) == false) 864 | return GEPD::ExitStatus::write_overflow; 865 | if (ei_encode_string_len(send_buffer.get(), &index, 866 | stream.get(), i_flush + 1)) 867 | return GEPD::ExitStatus::ei_encode_error; 868 | int status; 869 | if ((status = write_cmd(send_buffer, index - 870 | sizeof(OUTPUT_PREFIX_TYPE)))) 871 | return status; 872 | // keep any data not yet sent (waiting for a newline) 873 | if (i_flush == i - 1) 874 | { 875 | i = 0; 876 | } 877 | else 878 | { 879 | size_t const remaining_bytes = i - i_flush - 1; 880 | stream.move(i_flush + 1, remaining_bytes, 0); 881 | i = remaining_bytes; 882 | } 883 | } 884 | return GEPD::ExitStatus::success; 885 | } 886 | 887 | int GEPD::flush_stream(int fd, short revents, 888 | char const * const name, unsigned long const pid, 889 | realloc_ptr & send_buffer, 890 | realloc_ptr & stream, size_t & i) 891 | { 892 | if ((revents & POLLIN) == false) 893 | return GEPD::ExitStatus::success; 894 | 895 | ssize_t left = stream.size() - i; 896 | assert(left > 0); 897 | ssize_t read_bytes; 898 | while ((read_bytes = read(fd, &stream[i], left)) == left && 899 | stream.grow()) 900 | { 901 | i += left; 902 | left = stream.size() - i; 903 | bool ready = true; 904 | data_ready(fd, ready); 905 | if (ready == false) 906 | break; 907 | } 908 | if (read_bytes == 0 && i == 0) 909 | return GEPD::ExitStatus::success; 910 | else if (read_bytes != -1) 911 | i += read_bytes; // i is the next index to read at, always 912 | 913 | size_t const total = i - 1; 914 | i = 0; 915 | 916 | int index = sizeof(OUTPUT_PREFIX_TYPE); 917 | if (ei_encode_version(send_buffer.get(), &index)) 918 | return GEPD::ExitStatus::ei_encode_error; 919 | if (ei_encode_tuple_header(send_buffer.get(), &index, 3)) 920 | return GEPD::ExitStatus::ei_encode_error; 921 | if (ei_encode_atom(send_buffer.get(), &index, name)) 922 | return GEPD::ExitStatus::ei_encode_error; 923 | if (ei_encode_ulong(send_buffer.get(), &index, pid)) 924 | return GEPD::ExitStatus::ei_encode_error; 925 | if (send_buffer.reserve(index + total + 1) == false) 926 | return GEPD::ExitStatus::write_overflow; 927 | if (ei_encode_string_len(send_buffer.get(), &index, 928 | stream.get(), total)) 929 | return GEPD::ExitStatus::ei_encode_error; 930 | int status; 931 | if ((status = write_cmd(send_buffer, index - 932 | sizeof(OUTPUT_PREFIX_TYPE)))) 933 | return status; 934 | 935 | return GEPD::ExitStatus::success; 936 | } 937 | 938 | realloc_ptr GEPD::fds(4, 65536); 939 | nfds_t GEPD::nfds = 0; 940 | 941 | // main loop for handling inherently synchronous function calls 942 | // (a linked-in Erlang port driver that makes synchronous calls with 943 | // driver level locking should be similar to an Erlang port, except that 944 | // the port driver is a VM process and the port is an OS process) 945 | 946 | int GEPD::default_main() 947 | { 948 | // use the option {packet, 4} for open_port/2 949 | // (limited by 4MB buffer size below) 950 | realloc_ptr buffer(32768, 4194304); 951 | realloc_ptr stream1(1, 16384); 952 | realloc_ptr stream2(1, 16384); 953 | int status; 954 | if ((status = GEPD::init())) 955 | return status; 956 | int count; 957 | return GEPD::wait(count, buffer, stream1, stream2); 958 | } 959 | 960 | int GEPD::init() 961 | { 962 | if (nfds > 0) 963 | fds.move(0, nfds, 3); 964 | 965 | int status; 966 | if ((status = store_standard_fd(1, fds[INDEX_STDOUT].fd))) 967 | return status; 968 | fds[INDEX_STDOUT].events = POLLIN | POLLPRI; 969 | fds[INDEX_STDOUT].revents = 0; 970 | if ((status = store_standard_fd(2, fds[INDEX_STDERR].fd))) 971 | return status; 972 | fds[INDEX_STDERR].events = POLLIN | POLLPRI; 973 | fds[INDEX_STDERR].revents = 0; 974 | fds[INDEX_ERLANG].fd = PORT_READ_FILE_DESCRIPTOR; 975 | fds[INDEX_ERLANG].events = POLLIN | POLLPRI; 976 | fds[INDEX_ERLANG].revents = 0; 977 | nfds += 3; 978 | return GEPD::ExitStatus::success; 979 | } 980 | 981 | int GEPD::wait(int & count, 982 | realloc_ptr & buffer, 983 | realloc_ptr & stream1, 984 | realloc_ptr & stream2) 985 | { 986 | static unsigned long const ppid = getppid(); 987 | // based on TIMEOUT_TERMINATE_MAX in cloudi_core_i_constants.hrl 988 | int const timeout = 60000; // milliseconds 989 | int status; 990 | while ((status = GEPD::wait(count, timeout, buffer, 991 | stream1, stream2)) == GEPD::ExitStatus::timeout) 992 | { 993 | if (orphaned(ppid)) 994 | return GEPD::ExitStatus::erlang_exit; 995 | } 996 | if (status == GEPD::ExitStatus::ready) 997 | { 998 | if (orphaned(ppid)) 999 | status = GEPD::ExitStatus::erlang_exit; 1000 | } 1001 | return status; 1002 | } 1003 | 1004 | int GEPD::wait(int & count, int const timeout, 1005 | realloc_ptr & buffer, 1006 | realloc_ptr & stream1, 1007 | realloc_ptr & stream2) 1008 | { 1009 | static unsigned long const pid = getpid(); 1010 | static size_t index_stream1 = 0; 1011 | static size_t index_stream2 = 0; 1012 | while ((count = poll(fds.get(), nfds, timeout)) > 0) 1013 | { 1014 | int status; 1015 | if (count > 0 && fds[INDEX_ERLANG].revents != 0) 1016 | { 1017 | if ((status = consume_erlang(fds[INDEX_ERLANG].revents, buffer))) 1018 | return status; 1019 | --count; 1020 | } 1021 | fflush(stdout); 1022 | fflush(stderr); 1023 | if (count > 0 && fds[INDEX_STDERR].revents != 0) 1024 | { 1025 | if ((status = consume_stream(fds[INDEX_STDERR].fd, 1026 | fds[INDEX_STDERR].revents, 1027 | "stderr", pid, buffer, 1028 | stream2, index_stream2))) 1029 | return status; 1030 | --count; 1031 | } 1032 | if (count > 0 && fds[INDEX_STDOUT].revents != 0) 1033 | { 1034 | if ((status = consume_stream(fds[INDEX_STDOUT].fd, 1035 | fds[INDEX_STDOUT].revents, 1036 | "stdout", pid, buffer, 1037 | stream1, index_stream1))) 1038 | return status; 1039 | --count; 1040 | } 1041 | if (count > 0) 1042 | return GEPD::ExitStatus::ready; 1043 | } 1044 | if (count == 0) 1045 | return GEPD::ExitStatus::timeout; 1046 | else 1047 | return errno_poll(); 1048 | } 1049 | 1050 | -------------------------------------------------------------------------------- /port.hpp: -------------------------------------------------------------------------------- 1 | //-*-Mode:C++;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | // ex: set ft=cpp fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | ////////////////////////////////////////////////////////////////////////////// 4 | // 5 | // GENERIC ERLANG PORT [DRIVER] 6 | // automatically create Erlang bindings to C++/C that requires an OS process 7 | // 8 | // MIT License 9 | // 10 | // Copyright (c) 2009-2020 Michael Truog 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a 13 | // copy of this software and associated documentation files (the "Software"), 14 | // to deal in the Software without restriction, including without limitation 15 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 | // and/or sell copies of the Software, and to permit persons to whom the 17 | // Software is furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | // DEALINGS IN THE SOFTWARE. 29 | ////////////////////////////////////////////////////////////////////////////// 30 | 31 | #ifndef GEPD_PORT_HPP 32 | #define GEPD_PORT_HPP 33 | 34 | #include 35 | #include "realloc_ptr.hpp" 36 | 37 | namespace GEPD 38 | { 39 | namespace ExitStatus 40 | { 41 | int const success = 0; 42 | int const min = 78; // all GEPD values >= 43 | int const ready = 78; // external fd is ready 44 | int const timeout = 79; // timeout on all fds 45 | int const errors_min = 80; // errors >= 46 | int const erlang_exit = errors_min; 47 | int const read_EAGAIN = errors_min + 1; 48 | int const read_EBADF = errors_min + 2; 49 | int const read_EFAULT = errors_min + 3; 50 | int const read_EINTR = errors_min + 4; 51 | int const read_EINVAL = errors_min + 5; 52 | int const read_EIO = errors_min + 6; 53 | int const read_EISDIR = errors_min + 7; 54 | int const read_null = errors_min + 8; 55 | int const read_overflow = errors_min + 9; 56 | int const read_unknown = errors_min + 10; 57 | int const write_EAGAIN = errors_min + 11; 58 | int const write_EBADF = errors_min + 12; 59 | int const write_EFAULT = errors_min + 13; 60 | int const write_EFBIG = errors_min + 14; 61 | int const write_EINTR = errors_min + 15; 62 | int const write_EINVAL = errors_min + 16; 63 | int const write_EIO = errors_min + 17; 64 | int const write_ENOSPC = errors_min + 18; 65 | int const write_EPIPE = errors_min + 19; 66 | int const write_null = errors_min + 20; 67 | int const write_overflow = errors_min + 21; 68 | int const write_unknown = errors_min + 22; 69 | int const ei_encode_error = errors_min + 23; 70 | int const poll_EBADF = errors_min + 24; 71 | int const poll_EFAULT = errors_min + 25; 72 | int const poll_EINTR = errors_min + 26; 73 | int const poll_EINVAL = errors_min + 27; 74 | int const poll_ENOMEM = errors_min + 28; 75 | int const poll_ERR = errors_min + 29; 76 | int const poll_HUP = errors_min + 30; 77 | int const poll_NVAL = errors_min + 31; 78 | int const poll_unknown = errors_min + 32; 79 | int const pipe_EFAULT = errors_min + 33; 80 | int const pipe_EINVAL = errors_min + 34; 81 | int const pipe_EMFILE = errors_min + 35; 82 | int const pipe_ENFILE = errors_min + 36; 83 | int const pipe_unknown = errors_min + 37; 84 | int const dup_EBADF = errors_min + 38; 85 | int const dup_EBUSY = errors_min + 39; 86 | int const dup_EINTR = errors_min + 40; 87 | int const dup_EINVAL = errors_min + 41; 88 | int const dup_EMFILE = errors_min + 42; 89 | int const dup_unknown = errors_min + 43; 90 | int const close_EBADF = errors_min + 44; 91 | int const close_EINTR = errors_min + 45; 92 | int const close_EIO = errors_min + 46; 93 | int const close_unknown = errors_min + 47; 94 | int const errors_max = errors_min + 48; // errors < 95 | int const error_HUP = poll_HUP; 96 | } 97 | 98 | int consume_stream(int fd, short & revents, 99 | char const * const name, unsigned long const pid, 100 | realloc_ptr & send_buffer, 101 | realloc_ptr & stream, size_t & i); 102 | 103 | int flush_stream(int fd, short revents, 104 | char const * const name, unsigned long const pid, 105 | realloc_ptr & send_buffer, 106 | realloc_ptr & stream, size_t & i); 107 | 108 | extern realloc_ptr fds; 109 | extern nfds_t nfds; 110 | 111 | int default_main(); 112 | int init(); 113 | int wait(int & count, 114 | realloc_ptr & buffer, 115 | realloc_ptr & stream1, 116 | realloc_ptr & stream2); 117 | int wait(int & count, int const timeout, 118 | realloc_ptr & buffer, 119 | realloc_ptr & stream1, 120 | realloc_ptr & stream2); 121 | } 122 | 123 | #endif // GEPD_PORT_HPP 124 | 125 | -------------------------------------------------------------------------------- /port_driver.cpp: -------------------------------------------------------------------------------- 1 | //-*-Mode:C++;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | // ex: set ft=cpp fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | ////////////////////////////////////////////////////////////////////////////// 4 | // 5 | // GENERIC ERLANG PORT [DRIVER] 6 | // automatically create Erlang bindings to C++/C that requires an OS process 7 | // 8 | // MIT License 9 | // 10 | // Copyright (c) 2009-2017 Michael Truog 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a 13 | // copy of this software and associated documentation files (the "Software"), 14 | // to deal in the Software without restriction, including without limitation 15 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 | // and/or sell copies of the Software, and to permit persons to whom the 17 | // Software is furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | // DEALINGS IN THE SOFTWARE. 29 | ////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include 32 | // R15 type for pre-R15 port driver compilation 33 | #if ERL_DRV_EXTENDED_MAJOR_VERSION == 1 34 | typedef int ErlDrvSizeT; 35 | typedef int ErlDrvSSizeT; 36 | #elif ((ERL_DRV_EXTENDED_MAJOR_VERSION == 2 && \ 37 | ERL_DRV_EXTENDED_MINOR_VERSION == 1) || \ 38 | ERL_DRV_EXTENDED_MAJOR_VERSION > 2) 39 | #define ERLANG_R16_SUPPORT 40 | #endif 41 | #if ((ERL_DRV_EXTENDED_MAJOR_VERSION == 3 && \ 42 | ERL_DRV_EXTENDED_MINOR_VERSION >= 3) || \ 43 | ERL_DRV_EXTENDED_MAJOR_VERSION > 3) 44 | #define ERLANG_19_SUPPORT 45 | #endif 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | #include 53 | //#include // broken with boost >= 1.5? 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | // work-around instead of BOOST_PP_TUPLE_TO_SEQ to correctly handle arity 0 73 | #define TUPLE_TO_SEQ_E(r, data, elem) (elem) 74 | #define TUPLE_TO_SEQ(I, T) \ 75 | BOOST_PP_LIST_FOR_EACH(TUPLE_TO_SEQ_E, _, BOOST_PP_TUPLE_TO_LIST(I, T)) 76 | 77 | #include "pchar_len_t.h" 78 | 79 | // port driver documentation: 80 | // http://erlang.org/doc/man/erl_driver.html 81 | // http://erlang.org/doc/man/erl_ddll.html 82 | 83 | // limit on the number of function arguments handled in the bindings 84 | // (increasing this increases the (heap) memory consumption for 85 | // asynchronous function calls and the stack size of synchronous calls) 86 | #define PORT_DRIVER_FUNCTIONS_MAXIMUM_ARGUMENTS 9 87 | 88 | #if ! defined(PORT_DRIVER_NAME) 89 | #error Define PORT_DRIVER_NAME within the functions header file to specify the \ 90 | dynamic library name (should not be static) 91 | #endif 92 | #if ! defined(PORT_DRIVER_FUNCTIONS) 93 | #if defined(PORT_FUNCTIONS) 94 | 95 | #define CREATE_PORT_DRIVER_FUNCTIONS_DEFINITION(S, DATA, ELEMENT) (\ 96 | BOOST_PP_TUPLE_ELEM(4, 0, ELEMENT),\ 97 | BOOST_PP_TUPLE_ELEM(4, 1, ELEMENT),\ 98 | BOOST_PP_TUPLE_ELEM(4, 2, ELEMENT),\ 99 | BOOST_PP_TUPLE_ELEM(4, 3, ELEMENT), 0)\ 100 | 101 | #define PORT_DRIVER_FUNCTIONS \ 102 | BOOST_PP_SEQ_TRANSFORM(CREATE_PORT_DRIVER_FUNCTIONS_DEFINITION, _, \ 103 | PORT_FUNCTIONS) 104 | #warning Using PORT_FUNCTIONS to determine PORT_DRIVER_FUNCTIONS \ 105 | (all synchronous function calls) 106 | #else 107 | #error Define PORT_DRIVER_FUNCTIONS within the functions header file to \ 108 | specify the functions and their types 109 | #endif 110 | #endif 111 | 112 | // 64bit return values are only possible on 64bit machines 113 | // because of the types ErlDrvSInt and ErlDrvUInt 114 | #if INTPTR_MAX >= INT64_MAX 115 | #define NATIVE_64BIT_TYPES 1 116 | #endif 117 | 118 | // define the structure of the PORT_DRIVER_FUNCTIONS macro data 119 | // (sequence of tuples) 120 | 121 | // 5 tuple elements in the PORT_DRIVER_FUNCTIONS sequence 122 | #define PORT_DRIVER_FUNCTION_ENTRY_LENGTH 5 123 | // specific tuple elements in the PORT_DRIVER_FUNCTIONS sequence 124 | #define PORT_DRIVER_FUNCTION_ENTRY_NAME 0 125 | #define PORT_DRIVER_FUNCTION_ENTRY_ARGC 1 126 | #define PORT_DRIVER_FUNCTION_ENTRY_ARGV 2 127 | #define PORT_DRIVER_FUNCTION_ENTRY_RETURN 3 128 | #define PORT_DRIVER_FUNCTION_ENTRY_ASYNC 4 129 | 130 | // macros to access function data in a PORT_DRIVER_FUNCTIONS tuple entry 131 | 132 | #define GET_NAME(FUNCTION) \ 133 | BOOST_PP_TUPLE_ELEM(\ 134 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 135 | PORT_DRIVER_FUNCTION_ENTRY_NAME, FUNCTION\ 136 | ) 137 | #define GET_ARGC(FUNCTION) \ 138 | BOOST_PP_TUPLE_ELEM(\ 139 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 140 | PORT_DRIVER_FUNCTION_ENTRY_ARGC, FUNCTION\ 141 | ) 142 | #define GET_ARGV(FUNCTION) \ 143 | BOOST_PP_TUPLE_ELEM(\ 144 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 145 | PORT_DRIVER_FUNCTION_ENTRY_ARGV, FUNCTION\ 146 | ) 147 | #define GET_RETURN(FUNCTION) \ 148 | BOOST_PP_TUPLE_ELEM(\ 149 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 150 | PORT_DRIVER_FUNCTION_ENTRY_RETURN, FUNCTION\ 151 | ) 152 | #define GET_ASYNC(FUNCTION) \ 153 | BOOST_PP_TUPLE_ELEM(\ 154 | PORT_DRIVER_FUNCTION_ENTRY_LENGTH, \ 155 | PORT_DRIVER_FUNCTION_ENTRY_ASYNC, FUNCTION\ 156 | ) 157 | 158 | // enforce inherent implementation limits 159 | 160 | #if BOOST_PP_SEQ_SIZE(PORT_DRIVER_FUNCTIONS) > 32767 161 | #error Limited to 32767 port driver functions (type uint16_t is used for "cmd") 162 | #endif 163 | 164 | #if defined(PORT_DRIVER_C_FUNCTIONS_HEADER_FILE) 165 | extern "C" 166 | { 167 | #include PORT_DRIVER_C_FUNCTIONS_HEADER_FILE 168 | } 169 | #elif defined(PORT_DRIVER_CXX_FUNCTIONS_HEADER_FILE) 170 | #include PORT_DRIVER_CXX_FUNCTIONS_HEADER_FILE 171 | #else 172 | #error Neither PORT_DRIVER_C_FUNCTIONS_HEADER_FILE nor \ 173 | PORT_DRIVER_CXX_FUNCTIONS_HEADER_FILE are defined 174 | #endif 175 | 176 | // types available to generate bindings for 177 | // (limited list to provide efficient bindings that do not copy the arguments 178 | // to a temporary (char *) buffer, only real types that are usable as both 179 | // argument types and return value types) 180 | #include 181 | #define PORT_DRIVER_AVAILABLE_TYPES \ 182 | (bool)(char)(int8_t)(uint8_t)\ 183 | (int16_t)(uint16_t)\ 184 | (int32_t)(uint32_t)(time_t)\ 185 | (int64_t)(uint64_t)(double) 186 | // possible return value types include: void, pchar, uchar, float 187 | // possible argument types include: pchar_len, uchar 188 | 189 | // macros that define type handling in the bindings for 190 | // the function arguments and return value 191 | // (adding to PORT_DRIVER_AVAILABLE_TYPES requires additions below) 192 | 193 | // bool 194 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_bool(PREFIX) \ 195 | (BOOST_PP_CAT(PREFIX, _._bool)) 196 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_bool(ASYNC, PREFIX, ARG) \ 197 | if (EV_GET_UINT8(ev, &(ARG), p, q))\ 198 | {\ 199 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 200 | Error::decode_arguments);\ 201 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 202 | return;\ 203 | } 204 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_bool(PREFIX, ARG) \ 205 | BOOST_PP_EMPTY() 206 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_bool(PREFIX, ARG) \ 207 | BOOST_PP_EMPTY() 208 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_bool(PREFIX) \ 209 | BOOST_PP_CAT(PREFIX, bool) = 210 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_bool(CMD, ASYNC, PREFIX) \ 211 | reply_data_boolean(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, bool)); 212 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_bool \ 213 | BOOST_PP_EMPTY() 214 | 215 | // char 216 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_char(PREFIX) \ 217 | (BOOST_PP_CAT(PREFIX, _._char)) 218 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_char(ASYNC, PREFIX, ARG) \ 219 | if (EV_GET_UINT8(ev, &(ARG), p, q))\ 220 | {\ 221 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 222 | Error::decode_arguments);\ 223 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 224 | return;\ 225 | } 226 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_char(PREFIX, ARG) \ 227 | BOOST_PP_EMPTY() 228 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_char(PREFIX, ARG) \ 229 | BOOST_PP_EMPTY() 230 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_char(PREFIX) \ 231 | BOOST_PP_CAT(PREFIX, char) = 232 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_char(CMD, ASYNC, PREFIX) \ 233 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, char)); 234 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_char \ 235 | BOOST_PP_EMPTY() 236 | 237 | // uchar, unsigned char 238 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_uchar(PREFIX) \ 239 | (BOOST_PP_CAT(PREFIX, _._uchar)) 240 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_uchar(ASYNC, PREFIX, ARG) \ 241 | if (EV_GET_UINT8(ev, &(ARG), p, q))\ 242 | {\ 243 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 244 | Error::decode_arguments);\ 245 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 246 | return;\ 247 | } 248 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_uchar(PREFIX, ARG) \ 249 | BOOST_PP_EMPTY() 250 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_uchar(PREFIX, ARG) \ 251 | BOOST_PP_EMPTY() 252 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_uchar(PREFIX) \ 253 | BOOST_PP_CAT(PREFIX, uchar) = 254 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_uchar(CMD, ASYNC, PREFIX) \ 255 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, uchar)); 256 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_uchar \ 257 | BOOST_PP_EMPTY() 258 | 259 | // int8_t 260 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_int8_t(PREFIX) \ 261 | (BOOST_PP_CAT(PREFIX, _._int8_t)) 262 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_int8_t(ASYNC, PREFIX, ARG) \ 263 | if (EV_GET_UINT8(ev, &(ARG), p, q))\ 264 | {\ 265 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 266 | Error::decode_arguments);\ 267 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 268 | return;\ 269 | } 270 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_int8_t(PREFIX, ARG) \ 271 | BOOST_PP_EMPTY() 272 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_int8_t(PREFIX, ARG) \ 273 | BOOST_PP_EMPTY() 274 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_int8_t(PREFIX) \ 275 | BOOST_PP_CAT(PREFIX, int8_t) = 276 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_int8_t(CMD, ASYNC, PREFIX) \ 277 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, int8_t)); 278 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_int8_t \ 279 | BOOST_PP_EMPTY() 280 | 281 | // uint8_t 282 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_uint8_t(PREFIX) \ 283 | (BOOST_PP_CAT(PREFIX, _._uint8_t)) 284 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_uint8_t(ASYNC, PREFIX, ARG) \ 285 | if (EV_GET_UINT8(ev, &(ARG), p, q))\ 286 | {\ 287 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 288 | Error::decode_arguments);\ 289 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 290 | return;\ 291 | } 292 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_uint8_t(PREFIX, ARG) \ 293 | BOOST_PP_EMPTY() 294 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_uint8_t(PREFIX, ARG) \ 295 | BOOST_PP_EMPTY() 296 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_uint8_t(PREFIX) \ 297 | BOOST_PP_CAT(PREFIX, uint8_t) = 298 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_uint8_t(CMD, ASYNC, PREFIX) \ 299 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, uint8_t)); 300 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_uint8_t \ 301 | BOOST_PP_EMPTY() 302 | 303 | // int16_t 304 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_int16_t(PREFIX) \ 305 | (BOOST_PP_CAT(PREFIX, _._int16_t)) 306 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_int16_t(ASYNC, PREFIX, ARG) \ 307 | if (EV_GET_UINT16(ev, &(ARG), p, q))\ 308 | {\ 309 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 310 | Error::decode_arguments);\ 311 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 312 | return;\ 313 | } 314 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_int16_t(PREFIX, ARG) \ 315 | BOOST_PP_EMPTY() 316 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_int16_t(PREFIX, ARG) \ 317 | BOOST_PP_EMPTY() 318 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_int16_t(PREFIX) \ 319 | BOOST_PP_CAT(PREFIX, int16_t) = 320 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_int16_t(CMD, ASYNC, PREFIX) \ 321 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, int16_t)); 322 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_int16_t \ 323 | BOOST_PP_EMPTY() 324 | 325 | // uint16_t 326 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_uint16_t(PREFIX) \ 327 | (BOOST_PP_CAT(PREFIX, _._uint16_t)) 328 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_uint16_t(ASYNC, PREFIX, ARG) \ 329 | if (EV_GET_UINT16(ev, &(ARG), p, q))\ 330 | {\ 331 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 332 | Error::decode_arguments);\ 333 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 334 | return;\ 335 | } 336 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_uint16_t(PREFIX, ARG) \ 337 | BOOST_PP_EMPTY() 338 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_uint16_t(PREFIX, ARG) \ 339 | BOOST_PP_EMPTY() 340 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_uint16_t(PREFIX) \ 341 | BOOST_PP_CAT(PREFIX, uint16_t) = 342 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_uint16_t(CMD, ASYNC, PREFIX) \ 343 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, uint16_t)); 344 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_uint16_t \ 345 | BOOST_PP_EMPTY() 346 | 347 | // int32_t 348 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_int32_t(PREFIX) \ 349 | (BOOST_PP_CAT(PREFIX, _._int32_t)) 350 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_int32_t(ASYNC, PREFIX, ARG) \ 351 | if (EV_GET_UINT32(ev, &(ARG), p, q))\ 352 | {\ 353 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 354 | Error::decode_arguments);\ 355 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 356 | return;\ 357 | } 358 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_int32_t(PREFIX, ARG) \ 359 | BOOST_PP_EMPTY() 360 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_int32_t(PREFIX, ARG) \ 361 | BOOST_PP_EMPTY() 362 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_int32_t(PREFIX) \ 363 | BOOST_PP_CAT(PREFIX, int32_t) = 364 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_int32_t(CMD, ASYNC, PREFIX) \ 365 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, int32_t)); 366 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_int32_t \ 367 | BOOST_PP_EMPTY() 368 | 369 | // uint32_t 370 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_uint32_t(PREFIX) \ 371 | (BOOST_PP_CAT(PREFIX, _._uint32_t)) 372 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_uint32_t(ASYNC, PREFIX, ARG) \ 373 | if (EV_GET_UINT32(ev, &(ARG), p, q))\ 374 | {\ 375 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 376 | Error::decode_arguments);\ 377 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 378 | return;\ 379 | } 380 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_uint32_t(PREFIX, ARG) \ 381 | BOOST_PP_EMPTY() 382 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_uint32_t(PREFIX, ARG) \ 383 | BOOST_PP_EMPTY() 384 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_uint32_t(PREFIX) \ 385 | BOOST_PP_CAT(PREFIX, uint32_t) = 386 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_uint32_t(CMD, ASYNC, PREFIX) \ 387 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, uint32_t)); 388 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_uint32_t \ 389 | BOOST_PP_EMPTY() 390 | 391 | // time_t, from time.h 392 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_time_t(PREFIX) \ 393 | (BOOST_PP_CAT(PREFIX, _._time_t)) 394 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_time_t(ASYNC, PREFIX, ARG) \ 395 | if (EV_GET_UINT64(ev, &GET_FUNCTION_ARGUMENT_SEQ(uint64_t, PREFIX), p, q)) \ 396 | {\ 397 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 398 | Error::decode_arguments);\ 399 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 400 | return;\ 401 | }\ 402 | if (sizeof(time_t) == 4)\ 403 | {\ 404 | ARG = GET_FUNCTION_ARGUMENT_SEQ(uint64_t, PREFIX) & 0xffffffff;\ 405 | }\ 406 | else \ 407 | {\ 408 | ARG = GET_FUNCTION_ARGUMENT_SEQ(uint64_t, PREFIX);\ 409 | } 410 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_time_t(PREFIX, ARG) \ 411 | BOOST_PP_EMPTY() 412 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_time_t(PREFIX, ARG) \ 413 | BOOST_PP_EMPTY() 414 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_time_t(PREFIX) \ 415 | BOOST_PP_CAT(PREFIX, time_t) = 416 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_time_t(CMD, ASYNC, PREFIX) \ 417 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, time_t)); 418 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_time_t \ 419 | BOOST_PP_EMPTY() 420 | 421 | // int64_t 422 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_int64_t(PREFIX) \ 423 | (BOOST_PP_CAT(PREFIX, _._int64_t)) 424 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_int64_t(ASYNC, PREFIX, ARG) \ 425 | if (EV_GET_UINT64(ev, &(ARG), p, q))\ 426 | {\ 427 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 428 | Error::decode_arguments);\ 429 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 430 | return;\ 431 | } 432 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_int64_t(PREFIX, ARG) \ 433 | BOOST_PP_EMPTY() 434 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_int64_t(PREFIX, ARG) \ 435 | BOOST_PP_EMPTY() 436 | #ifdef NATIVE_64BIT_TYPES 437 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_int64_t(PREFIX) \ 438 | BOOST_PP_CAT(PREFIX, int64_t) = 439 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_int64_t(CMD, ASYNC, PREFIX) \ 440 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, int64_t)); 441 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_int64_t \ 442 | BOOST_PP_EMPTY() 443 | #endif 444 | 445 | // uint64_t 446 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_uint64_t(PREFIX) \ 447 | (BOOST_PP_CAT(PREFIX, _._uint64_t)) 448 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_uint64_t(ASYNC, PREFIX, ARG) \ 449 | if (EV_GET_UINT64(ev, &(ARG), p, q))\ 450 | {\ 451 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 452 | Error::decode_arguments);\ 453 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 454 | return;\ 455 | } 456 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_uint64_t(PREFIX, ARG) \ 457 | BOOST_PP_EMPTY() 458 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_uint64_t(PREFIX, ARG) \ 459 | BOOST_PP_EMPTY() 460 | #ifdef NATIVE_64BIT_TYPES 461 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_uint64_t(PREFIX) \ 462 | BOOST_PP_CAT(PREFIX, uint64_t) = 463 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_uint64_t(CMD, ASYNC, PREFIX) \ 464 | reply_data_integer(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, uint64_t)); 465 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_uint64_t \ 466 | BOOST_PP_EMPTY() 467 | #endif 468 | 469 | // double 470 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_double(PREFIX) \ 471 | (BOOST_PP_CAT(PREFIX, _._double)) 472 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_double(ASYNC, PREFIX, ARG) \ 473 | if (EV_GET_UINT64(ev, &(ARG), p, q))\ 474 | {\ 475 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 476 | Error::decode_arguments);\ 477 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 478 | return;\ 479 | } 480 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_double(PREFIX, ARG) \ 481 | BOOST_PP_EMPTY() 482 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_double(PREFIX, ARG) \ 483 | BOOST_PP_EMPTY() 484 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_double(PREFIX) \ 485 | BOOST_PP_CAT(PREFIX, double) = 486 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_double(CMD, ASYNC, PREFIX) \ 487 | reply_data_double(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, double)); 488 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_double \ 489 | BOOST_PP_EMPTY() 490 | 491 | // pchar_len, (char *, length) handled as one parameter 492 | // (does not copy the Erlang data, but uses the reference count) 493 | #define GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_pchar_len(PREFIX) \ 494 | (BOOST_PP_CAT(PREFIX, _._bin.ptr._char)) \ 495 | (BOOST_PP_CAT(PREFIX, _._bin.length)) 496 | #define CREATE_FUNCTION_INPUT_EV_STORE_TYPE_pchar_len(ASYNC, PREFIX, PTR, LEN) \ 497 | if (EV_GET_UINT32(ev, &(LEN), p, q)) \ 498 | {\ 499 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 500 | Error::decode_arguments);\ 501 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 502 | return;\ 503 | }\ 504 | BOOST_PP_CAT(PREFIX, _._bin.ptr._void) = EV_GETPOS(ev, p, q);\ 505 | BOOST_PP_CAT(PREFIX, _._bin.ref) = ev->binv[q]; \ 506 | if (ev_incr(ev, LEN, p, q) < 0) \ 507 | {\ 508 | reply_data_error_string(desc, c->cmd, ASYNC != 0, \ 509 | Error::decode_arguments);\ 510 | BOOST_PP_IF(ASYNC, driver_free(c); , BOOST_PP_EMPTY())\ 511 | return;\ 512 | } 513 | #define CREATE_FUNCTION_INPUT_PROCESS_TYPE_pchar_len(PREFIX, PTR, LEN) \ 514 | driver_binary_inc_refc(BOOST_PP_CAT(PREFIX, _._bin.ref)); 515 | #define CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_pchar_len(PREFIX, PTR, LEN) \ 516 | driver_binary_dec_refc(BOOST_PP_CAT(PREFIX, _._bin.ref)); 517 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t_nofree(PREFIX) \ 518 | BOOST_PP_CAT(PREFIX, pchar_len) = 519 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_len_t_nofree \ 520 | BOOST_PP_EMPTY() 521 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_len_t_nofree(CMD, ASYNC, PREFIX) \ 522 | reply_data_binary(\ 523 | desc, \ 524 | CMD, \ 525 | ASYNC != 0, \ 526 | BOOST_PP_CAT(PREFIX, pchar_len.pchar),\ 527 | BOOST_PP_CAT(PREFIX, pchar_len.length)\ 528 | ); 529 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t_free(PREFIX) \ 530 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t_nofree(PREFIX) 531 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_len_t_free \ 532 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_len_t_nofree 533 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_len_t_free(CMD, ASYNC, PREFIX) \ 534 | CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_len_t_nofree(CMD, ASYNC, PREFIX) \ 535 | free(BOOST_PP_CAT(PREFIX, pchar_len.pchar)); 536 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t(PREFIX) \ 537 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_len_t_free(PREFIX) 538 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_len_t \ 539 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_len_t_free 540 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_len_t(CMD, ASYNC, PREFIX) \ 541 | CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_len_t_free(CMD, ASYNC, PREFIX) 542 | 543 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_nofree(PREFIX) \ 544 | BOOST_PP_CAT(PREFIX, bin.ptr._char) = 545 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_nofree \ 546 | c->o._bin.length = strlen(c->o._bin.ptr._char); 547 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_nofree(CMD, ASYNC, \ 548 | PREFIX) \ 549 | reply_data_string(\ 550 | desc, \ 551 | CMD, \ 552 | ASYNC != 0, \ 553 | BOOST_PP_CAT(PREFIX, bin.ptr._char),\ 554 | BOOST_PP_CAT(PREFIX, bin.length)\ 555 | ); 556 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_free(PREFIX) \ 557 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_nofree(PREFIX) 558 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_free \ 559 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_nofree 560 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_free(CMD, ASYNC, \ 561 | PREFIX) \ 562 | CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_nofree(CMD, ASYNC, PREFIX) \ 563 | free(BOOST_PP_CAT(PREFIX, bin.ptr._char)); 564 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar(PREFIX) \ 565 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_pchar_nofree(PREFIX) 566 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar \ 567 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_pchar_nofree 568 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar(CMD, ASYNC, PREFIX) \ 569 | CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_pchar_nofree(CMD, ASYNC, PREFIX) 570 | 571 | // float 572 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_float(PREFIX) \ 573 | BOOST_PP_CAT(PREFIX, float) = 574 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_float(CMD, ASYNC, PREFIX) \ 575 | reply_data_double(desc, CMD, ASYNC != 0, BOOST_PP_CAT(PREFIX, float)); 576 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_float \ 577 | BOOST_PP_EMPTY() 578 | 579 | // void, return value handling 580 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_void(PREFIX) \ 581 | BOOST_PP_EMPTY() 582 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_void \ 583 | BOOST_PP_EMPTY() 584 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_void(CMD, ASYNC, PREFIX) \ 585 | reply_data_ok(desc, CMD, ASYNC != 0); 586 | 587 | ////////////////////////////////////////////////////////////////////////////// 588 | // preprocessing macros to generate function specific bindings code 589 | ////////////////////////////////////////////////////////////////////////////// 590 | 591 | // create the local invocations of the port driver functions 592 | 593 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE(TYPE, PREFIX) \ 594 | BOOST_PP_CAT(\ 595 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE_TYPE_, TYPE\ 596 | )(PREFIX) 597 | 598 | #define CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE(TYPE) \ 599 | BOOST_PP_CAT(\ 600 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE_TYPE_, TYPE\ 601 | ) 602 | 603 | #define GET_FUNCTION_ARGUMENT_SEQ(TYPE, PREFIX) \ 604 | BOOST_PP_CAT(\ 605 | GET_FUNCTION_ARGUMENT_SEQ_FROM_TYPE_, TYPE\ 606 | )(PREFIX) 607 | 608 | #define GET_FUNCTION_ARGUMENTS(SEQ) \ 609 | BOOST_PP_TUPLE_REM_CTOR(\ 610 | BOOST_PP_SEQ_SIZE(SEQ),\ 611 | BOOST_PP_SEQ_TO_TUPLE(SEQ)\ 612 | ) 613 | 614 | #define CREATE_INVOKE_FUNCTION_ARGUMENTS(Z, N, ARGUMENTS) \ 615 | GET_FUNCTION_ARGUMENTS(GET_FUNCTION_ARGUMENT_SEQ(\ 616 | BOOST_PP_SEQ_ELEM(N, ARGUMENTS),\ 617 | BOOST_PP_CAT(c->i.arg, N)\ 618 | )) 619 | 620 | #define CREATE_INVOKE_FUNCTION(I, DATA, FUNCTION) \ 621 | static void BOOST_PP_CAT(invoke_, GET_NAME(FUNCTION)) (void * data) \ 622 | { \ 623 | callstate_t *c = (callstate_t *) data; \ 624 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_STORE(GET_RETURN(FUNCTION), c->o._) \ 625 | GET_NAME(FUNCTION) \ 626 | BOOST_PP_LPAREN() \ 627 | BOOST_PP_ENUM( \ 628 | GET_ARGC(FUNCTION), \ 629 | CREATE_INVOKE_FUNCTION_ARGUMENTS, \ 630 | TUPLE_TO_SEQ(GET_ARGC(FUNCTION), GET_ARGV(FUNCTION)) \ 631 | ) \ 632 | BOOST_PP_RPAREN() \ 633 | ; \ 634 | CREATE_INVOKE_FUNCTION_RETURN_VALUE_PROCESSING_CODE(GET_RETURN(FUNCTION))\ 635 | } 636 | 637 | // create the case statements for each function for the initial call 638 | // (Erlang -> C++/C) 639 | 640 | #define CREATE_FUNCTION_INPUT_EV_STORE(TYPE, ARGUMENT) \ 641 | BOOST_PP_EXPAND( \ 642 | CREATE_FUNCTION_INPUT_EV_STORE_TYPE_ ## TYPE ARGUMENT \ 643 | ) 644 | 645 | #define CREATE_FUNCTION_INPUT_ARGUMENT_HANDLING(R, FUNCTION, I, TYPE) \ 646 | CREATE_FUNCTION_INPUT_EV_STORE(\ 647 | TYPE, BOOST_PP_SEQ_TO_TUPLE(\ 648 | (GET_ASYNC(FUNCTION))\ 649 | (BOOST_PP_CAT(c->i.arg, I))\ 650 | GET_FUNCTION_ARGUMENT_SEQ(TYPE, BOOST_PP_CAT(c->i.arg, I))\ 651 | )\ 652 | ) 653 | 654 | #define CREATE_FUNCTION_INPUT_PROCESS(TYPE, ARGUMENT) \ 655 | BOOST_PP_EXPAND( \ 656 | CREATE_FUNCTION_INPUT_PROCESS_TYPE_ ## TYPE ARGUMENT \ 657 | ) 658 | 659 | #define CREATE_FUNCTION_INPUT_ARGUMENT_PROCESSING(R, DATA, I, TYPE) \ 660 | CREATE_FUNCTION_INPUT_PROCESS(\ 661 | TYPE, BOOST_PP_SEQ_TO_TUPLE(\ 662 | (BOOST_PP_CAT(c->i.arg, I))\ 663 | GET_FUNCTION_ARGUMENT_SEQ(TYPE, BOOST_PP_CAT(c->i.arg, I))\ 664 | )\ 665 | ) 666 | 667 | #define CREATE_FUNCTION_INPUT_CASE(I, DATA, FUNCTION) \ 668 | case BOOST_PP_DEC(I):\ 669 | {\ 670 | BOOST_PP_IF(\ 671 | GET_ASYNC(FUNCTION),\ 672 | callstate_t * c = \ 673 | reinterpret_cast(driver_alloc(sizeof(callstate_t)));\ 674 | if (! c)\ 675 | {\ 676 | driver_failure_posix(desc->port, ENOMEM); \ 677 | return;\ 678 | } ,\ 679 | callstate_t sync_call; \ 680 | callstate_t * c = &sync_call;\ 681 | )\ 682 | c->desc = desc;\ 683 | c->cmd = cmd;\ 684 | c->invoke = BOOST_PP_CAT(invoke_, GET_NAME(FUNCTION)); \ 685 | BOOST_PP_SEQ_FOR_EACH_I(\ 686 | CREATE_FUNCTION_INPUT_ARGUMENT_HANDLING, FUNCTION,\ 687 | TUPLE_TO_SEQ(GET_ARGC(FUNCTION), GET_ARGV(FUNCTION))\ 688 | )\ 689 | BOOST_PP_SEQ_FOR_EACH_I(\ 690 | CREATE_FUNCTION_INPUT_ARGUMENT_PROCESSING, _,\ 691 | TUPLE_TO_SEQ(GET_ARGC(FUNCTION), GET_ARGV(FUNCTION))\ 692 | )\ 693 | BOOST_PP_IF(\ 694 | GET_ASYNC(FUNCTION),\ 695 | driver_async(desc->port, 0, c->invoke, c, driver_free);,\ 696 | (*(c->invoke))(reinterpret_cast(c));\ 697 | driver_entry_ready_async(\ 698 | reinterpret_cast(desc),\ 699 | reinterpret_cast(c)\ 700 | );\ 701 | )\ 702 | }\ 703 | return; 704 | 705 | // create the case statements for each function's return value 706 | // (C++/C -> Erlang) 707 | 708 | #define CREATE_FUNCTION_OUTPUT_RETURN_VALUE(TYPE, CMD, ASYNC, PREFIX) \ 709 | BOOST_PP_CAT(\ 710 | CREATE_FUNCTION_OUTPUT_RETURN_VALUE_TYPE_, TYPE \ 711 | )(CMD, ASYNC, PREFIX) 712 | 713 | #define CREATE_FUNCTION_OUTPUT_PROCESS(TYPE, ARGUMENT) \ 714 | BOOST_PP_EXPAND( \ 715 | CREATE_FUNCTION_OUTPUT_PROCESS_TYPE_ ## TYPE ARGUMENT \ 716 | ) 717 | 718 | #define CREATE_FUNCTION_OUTPUT_ARGUMENT_PROCESSING(R, DATA, I, TYPE) \ 719 | CREATE_FUNCTION_OUTPUT_PROCESS(\ 720 | TYPE, BOOST_PP_SEQ_TO_TUPLE(\ 721 | (BOOST_PP_CAT(c->i.arg, I))\ 722 | GET_FUNCTION_ARGUMENT_SEQ(TYPE, BOOST_PP_CAT(c->i.arg, I))\ 723 | )\ 724 | ) 725 | 726 | #define CREATE_FUNCTION_OUTPUT_CASE(I, DATA, FUNCTION) \ 727 | case BOOST_PP_DEC(I):\ 728 | CREATE_FUNCTION_OUTPUT_RETURN_VALUE(\ 729 | GET_RETURN(FUNCTION), c->cmd, GET_ASYNC(FUNCTION), c->o._\ 730 | )\ 731 | BOOST_PP_SEQ_FOR_EACH_I(\ 732 | CREATE_FUNCTION_OUTPUT_ARGUMENT_PROCESSING, _,\ 733 | TUPLE_TO_SEQ(GET_ARGC(FUNCTION), GET_ARGV(FUNCTION))\ 734 | )\ 735 | BOOST_PP_IF(GET_ASYNC(FUNCTION), driver_free(c); , BOOST_PP_EMPTY())\ 736 | return; 737 | 738 | ////////////////////////////////////////////////////////////////////////////// 739 | // code to handle access to ErlIOVec 740 | ////////////////////////////////////////////////////////////////////////////// 741 | 742 | // macros based on erts/emulator/drivers/common/efile_drv.c 743 | 744 | // char *EV_CHAR_P(ErlIOVec *ev, char p, int q) 745 | #define EV_CHAR_P(ev, p, q) \ 746 | (((char *)(ev)->iov[(q)].iov_base) + (p)) 747 | 748 | // char *EV_UCHAR_P(ErlIOVec *ev, unsigned char p, int q) 749 | #define EV_UCHAR_P(ev, p, q) \ 750 | (((unsigned char *)(ev)->iov[(q)].iov_base) + (p)) 751 | 752 | // int EV_GET_CHAR(ErlIOVec *ev, char *p, size_t &p, size_t &q) 753 | #define EV_GET_CHAR(ev, ptr, p, q) \ 754 | (p + 1 <= (ev)->iov[q].iov_len \ 755 | ? (*(ptr) = *EV_CHAR_P(ev, p, q), \ 756 | p = (p + 1 < (ev)->iov[q].iov_len \ 757 | ? p + 1 \ 758 | : (q++, 0)), \ 759 | 0) \ 760 | : !0) 761 | 762 | #define EV_GET_UINT8(ev, p, pp, qp) EV_GET_CHAR(ev, p, pp, qp) 763 | 764 | // int EV_GET_UINT16(ErlIOVec *ev, uint16_t *p, size_t &p, size_t &q) 765 | #define EV_GET_UINT16(ev, ptr, p, q) \ 766 | (p + 2 <= (ev)->iov[q].iov_len \ 767 | ? (*((uint16_t *) ptr) = (*((uint16_t *) EV_UCHAR_P(ev, p, q))), \ 768 | p = (p + 2 < (ev)->iov[q].iov_len \ 769 | ? p + 2 \ 770 | : (q++, 0)), \ 771 | 0) \ 772 | : !0) 773 | 774 | // int EV_GET_UINT32(ErlIOVec *ev, uint32_t *p, size_t &p, size_t &q) 775 | #define EV_GET_UINT32(ev, ptr, p, q) \ 776 | (p + 4 <= (ev)->iov[q].iov_len \ 777 | ? (*((uint32_t *) ptr) = (*((uint32_t *) EV_UCHAR_P(ev, p, q))), \ 778 | p = (p + 4 < (ev)->iov[q].iov_len \ 779 | ? p + 4 \ 780 | : (q++, 0)), \ 781 | 0) \ 782 | : !0) 783 | 784 | // int EV_GET_UINT64(ErlIOVec *ev, uint64_t *p, size_t &p, size_t &q) 785 | #define EV_GET_UINT64(ev, ptr, p, q) \ 786 | (p + 8 <= (ev)->iov[q].iov_len \ 787 | ? (*((uint64_t *) ptr) = (*((uint64_t *) EV_UCHAR_P(ev, p, q))), \ 788 | p = (p + 8 < (ev)->iov[q].iov_len \ 789 | ? p + 8 \ 790 | : (q++, 0)), \ 791 | 0) \ 792 | : !0) 793 | 794 | // void * EV_GETPOS(ErlIOVec *ev, size_t &p, size_t &q) 795 | #define EV_GETPOS(ev, p, q) \ 796 | ((q) < ((ev)->vsize < 0 ? 0 : (size_t) (ev)->vsize) \ 797 | ? ((ev)->iov[(q)].iov_base + p) \ 798 | : 0) 799 | 800 | /// increment the position within the ErlIOVec by the size n 801 | /// 802 | /// @return -1 on error, 0 no more data, 1 more data 803 | static int ev_incr(ErlIOVec *ev, size_t n, size_t & p, size_t & q) 804 | { 805 | const size_t pos = p + n; 806 | if (ev->vsize < 0) 807 | return -1; 808 | 809 | if (q >= static_cast(ev->vsize)) 810 | return -1; 811 | 812 | if (pos < ev->iov[q].iov_len) 813 | { 814 | p += n; 815 | return 1; 816 | } 817 | else if (pos == ev->iov[q].iov_len) 818 | { 819 | q++; 820 | p = 0; 821 | if (q < static_cast(ev->vsize)) 822 | return 1; 823 | else 824 | return 0; 825 | } 826 | else 827 | { 828 | return -1; 829 | } 830 | } 831 | 832 | ////////////////////////////////////////////////////////////////////////////// 833 | // data structures for managing driver data and function call data 834 | ////////////////////////////////////////////////////////////////////////////// 835 | 836 | // port instance of driver 837 | typedef struct 838 | { 839 | ErlDrvPort port; 840 | ErlDrvTermData port_term; 841 | ErlDrvMutex *driver_output_term_lock; 842 | } descriptor_t; 843 | 844 | // port driver function call state 845 | #define CREATE_COMMON_TYPE_ENTRIES(I, DATA, TYPE) TYPE BOOST_PP_CAT(_, TYPE) ; 846 | typedef struct 847 | { 848 | descriptor_t *desc; 849 | uint16_t cmd; 850 | void (*invoke)(void *); 851 | 852 | // function input parameters 853 | struct 854 | { 855 | #define CREATE_CALLSTATE_ARGUMENT(Z, X, DATA) \ 856 | union \ 857 | { \ 858 | BOOST_PP_SEQ_FOR_EACH(CREATE_COMMON_TYPE_ENTRIES, _, \ 859 | PORT_DRIVER_AVAILABLE_TYPES) \ 860 | unsigned char _uchar; \ 861 | struct \ 862 | { \ 863 | uint32_t length; \ 864 | union \ 865 | { \ 866 | void * _void; \ 867 | char * _char; \ 868 | } ptr; \ 869 | ErlDrvBinary *ref; \ 870 | } _bin; \ 871 | } arg##X##_; 872 | 873 | BOOST_PP_REPEAT_FROM_TO(0, PORT_DRIVER_FUNCTIONS_MAXIMUM_ARGUMENTS, 874 | CREATE_CALLSTATE_ARGUMENT, _) 875 | 876 | } i; 877 | 878 | // function output return value 879 | union { 880 | BOOST_PP_SEQ_FOR_EACH(CREATE_COMMON_TYPE_ENTRIES, _, 881 | PORT_DRIVER_AVAILABLE_TYPES) 882 | unsigned char _uchar; 883 | float _float; 884 | pchar_len_t _pchar_len; 885 | struct 886 | { 887 | uint32_t length; 888 | union 889 | { 890 | char * _char; 891 | void * _void; 892 | } ptr; 893 | } _bin; 894 | } o; 895 | 896 | } callstate_t; 897 | 898 | ////////////////////////////////////////////////////////////////////////////// 899 | // reply handling 900 | ////////////////////////////////////////////////////////////////////////////// 901 | 902 | static ErlDrvTermData const atom_value_ok = 903 | driver_mk_atom(const_cast("ok")); 904 | static ErlDrvTermData const atom_value_error = 905 | driver_mk_atom(const_cast("error")); 906 | static ErlDrvTermData const atom_value_data = 907 | driver_mk_atom(const_cast("data")); 908 | static ErlDrvTermData const atom_value_async = 909 | driver_mk_atom(const_cast("async")); 910 | static ErlDrvTermData const atom_value_true = 911 | driver_mk_atom(const_cast("true")); 912 | static ErlDrvTermData const atom_value_false = 913 | driver_mk_atom(const_cast("false")); 914 | 915 | #ifdef ERLANG_R16_SUPPORT 916 | static int driver_output_term_locked(descriptor_t * desc, 917 | ErlDrvTermData * term, 918 | ErlDrvSizeT n) 919 | { 920 | // Regarding erl_drv_output_term() in R16B: 921 | // "This function is only thread-safe when the emulator with 922 | // SMP support is used." 923 | // (http://www.erlang.org/doc/man/erl_driver.html#erl_drv_output_term) 924 | 925 | // ErlDrvSysInfo can not be checked since it is only provided to 926 | // system drivers, so locking will still be done manually 927 | erl_drv_mutex_lock(desc->driver_output_term_lock); 928 | int const returnValue = erl_drv_output_term(desc->port_term, term, n); 929 | erl_drv_mutex_unlock(desc->driver_output_term_lock); 930 | return returnValue; 931 | } 932 | #else 933 | static int driver_output_term_locked(descriptor_t * desc, 934 | ErlDrvTermData * term, 935 | ErlDrvSizeT n) 936 | { 937 | // Regarding driver_output_term() in R12B-5: 938 | // "Note that this function is not thread-safe, 939 | // not even when the emulator with SMP support is used." 940 | // (http://erlang.org/doc/man/erl_driver.html#driver_output_term) 941 | 942 | // since asynchronous thread pool threads could call this function, 943 | // in addition to a callback emulator thread, unstable behavior 944 | // could occur without a mutex lock 945 | erl_drv_mutex_lock(desc->driver_output_term_lock); 946 | int const returnValue = driver_output_term(desc->port, term, n); 947 | erl_drv_mutex_unlock(desc->driver_output_term_lock); 948 | return returnValue; 949 | } 950 | #endif 951 | 952 | static int reply_data_ok(descriptor_t * desc, uint16_t cmd, bool async) 953 | { 954 | ErlDrvTermData spec[] = { 955 | ERL_DRV_PORT, desc->port_term, 956 | ERL_DRV_ATOM, (async ? atom_value_async : atom_value_data), 957 | ERL_DRV_UINT, cmd, 958 | ERL_DRV_ATOM, atom_value_ok, 959 | ERL_DRV_TUPLE, 2, 960 | ERL_DRV_TUPLE, 2, 961 | ERL_DRV_TUPLE, 2 962 | }; 963 | return driver_output_term_locked(desc, 964 | spec, sizeof(spec) / sizeof(spec[0])); 965 | } 966 | 967 | #define CREATE_REPLY_OK_INTEGER(TYPE, ERLTYPE) \ 968 | static int reply_data_integer(descriptor_t * desc, uint16_t cmd, bool async, \ 969 | TYPE number) \ 970 | { \ 971 | ErlDrvTermData spec[] = { \ 972 | ERL_DRV_PORT, desc->port_term, \ 973 | ERL_DRV_ATOM, (async ? atom_value_async : atom_value_data), \ 974 | ERL_DRV_UINT, cmd, \ 975 | ERLTYPE, static_cast(number), \ 976 | ERL_DRV_TUPLE, 2, \ 977 | ERL_DRV_TUPLE, 2, \ 978 | ERL_DRV_TUPLE, 2 \ 979 | }; \ 980 | return driver_output_term_locked(desc, spec, \ 981 | sizeof(spec) / sizeof(spec[0])); \ 982 | } 983 | CREATE_REPLY_OK_INTEGER(char, ERL_DRV_INT) 984 | CREATE_REPLY_OK_INTEGER(int8_t, ERL_DRV_INT) 985 | CREATE_REPLY_OK_INTEGER(uint8_t, ERL_DRV_UINT) 986 | CREATE_REPLY_OK_INTEGER(int16_t, ERL_DRV_INT) 987 | CREATE_REPLY_OK_INTEGER(uint16_t, ERL_DRV_UINT) 988 | CREATE_REPLY_OK_INTEGER(int32_t, ERL_DRV_INT) 989 | CREATE_REPLY_OK_INTEGER(uint32_t, ERL_DRV_UINT) 990 | #ifdef NATIVE_64BIT_TYPES 991 | CREATE_REPLY_OK_INTEGER(int64_t, ERL_DRV_INT) 992 | CREATE_REPLY_OK_INTEGER(uint64_t, ERL_DRV_UINT) 993 | #endif 994 | 995 | static int reply_data_boolean(descriptor_t *desc, uint16_t cmd, bool async, 996 | bool value) 997 | { 998 | ErlDrvTermData spec[] = { 999 | ERL_DRV_PORT, desc->port_term, 1000 | ERL_DRV_ATOM, (async ? atom_value_async : atom_value_data), 1001 | ERL_DRV_UINT, cmd, 1002 | ERL_DRV_ATOM, (value ? atom_value_true : atom_value_false), 1003 | ERL_DRV_TUPLE, 2, 1004 | ERL_DRV_TUPLE, 2, 1005 | ERL_DRV_TUPLE, 2 1006 | }; 1007 | return driver_output_term_locked(desc, 1008 | spec, sizeof(spec) / sizeof(spec[0])); 1009 | } 1010 | 1011 | static int reply_data_double(descriptor_t *desc, uint16_t cmd, bool async, 1012 | double number) 1013 | { 1014 | ErlDrvTermData spec[] = { 1015 | ERL_DRV_PORT, desc->port_term, 1016 | ERL_DRV_ATOM, (async ? atom_value_async : atom_value_data), 1017 | ERL_DRV_UINT, cmd, 1018 | ERL_DRV_FLOAT, reinterpret_cast(&number), 1019 | ERL_DRV_TUPLE, 2, 1020 | ERL_DRV_TUPLE, 2, 1021 | ERL_DRV_TUPLE, 2 1022 | }; 1023 | return driver_output_term_locked(desc, 1024 | spec, sizeof(spec) / sizeof(spec[0])); 1025 | } 1026 | 1027 | static int reply_data_binary(descriptor_t *desc, uint16_t cmd, bool async, 1028 | ErlDrvBinary *ptr) 1029 | { 1030 | ErlDrvTermData spec[] = { 1031 | ERL_DRV_PORT, desc->port_term, 1032 | ERL_DRV_ATOM, (async ? atom_value_async : atom_value_data), 1033 | ERL_DRV_UINT, cmd, 1034 | ERL_DRV_BINARY, reinterpret_cast(ptr), 1035 | static_cast(ptr->orig_size), 0, 1036 | ERL_DRV_TUPLE, 2, 1037 | ERL_DRV_TUPLE, 2, 1038 | ERL_DRV_TUPLE, 2 1039 | }; 1040 | return driver_output_term_locked(desc, 1041 | spec, sizeof(spec) / sizeof(spec[0])); 1042 | } 1043 | 1044 | static int reply_data_binary(descriptor_t *desc, uint16_t cmd, bool async, 1045 | void *ptr, uint32_t length) 1046 | { 1047 | 1048 | ErlDrvTermData spec[] = { 1049 | ERL_DRV_PORT, desc->port_term, 1050 | ERL_DRV_ATOM, (async ? atom_value_async : atom_value_data), 1051 | ERL_DRV_UINT, cmd, 1052 | ERL_DRV_BUF2BINARY, reinterpret_cast(ptr), 1053 | length, 1054 | ERL_DRV_TUPLE, 2, 1055 | ERL_DRV_TUPLE, 2, 1056 | ERL_DRV_TUPLE, 2 1057 | }; 1058 | return driver_output_term_locked(desc, 1059 | spec, sizeof(spec) / sizeof(spec[0])); 1060 | } 1061 | 1062 | static int reply_data_string(descriptor_t *desc, uint16_t cmd, bool async, 1063 | char *ptr, uint32_t length) 1064 | { 1065 | ErlDrvTermData spec[] = { 1066 | ERL_DRV_PORT, desc->port_term, 1067 | ERL_DRV_ATOM, (async ? atom_value_async : atom_value_data), 1068 | ERL_DRV_UINT, cmd, 1069 | ERL_DRV_STRING, reinterpret_cast(ptr), 1070 | length, 1071 | ERL_DRV_TUPLE, 2, 1072 | ERL_DRV_TUPLE, 2, 1073 | ERL_DRV_TUPLE, 2 1074 | }; 1075 | return driver_output_term_locked(desc, 1076 | spec, sizeof(spec) / sizeof(spec[0])); 1077 | } 1078 | 1079 | namespace 1080 | { 1081 | // list of non-fatal errors that can be sent back 1082 | 1083 | // port driver will send an error for protocol problems 1084 | namespace Error 1085 | { 1086 | char const * const invalid_function = 1087 | "Invalid function call"; 1088 | char const * const decode_identifier = 1089 | "Unable to decode function identifier"; 1090 | char const * const decode_arguments = 1091 | "Unable to decode arguments"; 1092 | } 1093 | 1094 | } 1095 | 1096 | static int reply_data_error_string(descriptor_t *desc, uint16_t cmd, bool async, 1097 | char const * const ptr) 1098 | { 1099 | uint32_t const length = strlen(ptr); 1100 | ErlDrvTermData spec[] = { 1101 | ERL_DRV_PORT, desc->port_term, 1102 | ERL_DRV_ATOM, (async ? atom_value_async : atom_value_data), 1103 | ERL_DRV_ATOM, atom_value_error, 1104 | ERL_DRV_UINT, cmd, 1105 | ERL_DRV_STRING, reinterpret_cast(ptr), 1106 | length, 1107 | ERL_DRV_TUPLE, 3, 1108 | ERL_DRV_TUPLE, 2, 1109 | ERL_DRV_TUPLE, 2 1110 | }; 1111 | return driver_output_term_locked(desc, 1112 | spec, sizeof(spec) / sizeof(spec[0])); 1113 | } 1114 | 1115 | ////////////////////////////////////////////////////////////////////////////// 1116 | // driver implementation functions and data structure 1117 | ////////////////////////////////////////////////////////////////////////////// 1118 | 1119 | static int driver_entry_init() 1120 | { 1121 | return 0; 1122 | } 1123 | 1124 | static ErlDrvData driver_entry_start(ErlDrvPort port, char * /*args*/) 1125 | { 1126 | descriptor_t *desc = 1127 | reinterpret_cast(driver_alloc(sizeof(descriptor_t))); 1128 | if (! desc) 1129 | return ERL_DRV_ERROR_GENERAL; 1130 | desc->port = port; 1131 | desc->port_term = driver_mk_port(port); 1132 | desc->driver_output_term_lock = erl_drv_mutex_create( 1133 | const_cast("driver_output_term_lock")); 1134 | return reinterpret_cast(desc); 1135 | } 1136 | 1137 | static void driver_entry_stop(ErlDrvData driver_data) 1138 | { 1139 | descriptor_t *desc = reinterpret_cast(driver_data); 1140 | if (desc) 1141 | { 1142 | erl_drv_mutex_destroy(desc->driver_output_term_lock); 1143 | driver_free(desc); 1144 | } 1145 | } 1146 | 1147 | static void driver_entry_ready_async(ErlDrvData driver_data, 1148 | ErlDrvThreadData thread_data) 1149 | { 1150 | descriptor_t *desc = reinterpret_cast(driver_data); 1151 | callstate_t *c = reinterpret_cast(thread_data); 1152 | 1153 | if (! desc || ! c) 1154 | return; 1155 | 1156 | switch (c->cmd) 1157 | { 1158 | // create the case statements for each function 1159 | BOOST_PP_SEQ_FOR_EACH(CREATE_FUNCTION_OUTPUT_CASE, _, 1160 | PORT_DRIVER_FUNCTIONS) 1161 | default: 1162 | reply_data_error_string(desc, c->cmd, false, 1163 | Error::invalid_function); 1164 | return; 1165 | } 1166 | } 1167 | 1168 | // create the invoke functions that handle the call state and argument storage 1169 | BOOST_PP_SEQ_FOR_EACH(CREATE_INVOKE_FUNCTION, _, PORT_DRIVER_FUNCTIONS) 1170 | 1171 | static void driver_entry_outputv(ErlDrvData driver_data, ErlIOVec *ev) 1172 | { 1173 | descriptor_t *desc = reinterpret_cast(driver_data); 1174 | if (! desc || ! ev || ev->size < 1) 1175 | return; 1176 | 1177 | uint16_t cmd; 1178 | size_t q = 1; // index into ev->iov 1179 | size_t p = 0; // index into ev->iov[q].iov_base 1180 | if (EV_GET_UINT16(ev, &cmd, p, q)) 1181 | { 1182 | reply_data_error_string(desc, 0, false, Error::decode_identifier); 1183 | return; 1184 | } 1185 | 1186 | switch (cmd) 1187 | { 1188 | // create the case statements for each function 1189 | BOOST_PP_SEQ_FOR_EACH(CREATE_FUNCTION_INPUT_CASE, _, 1190 | PORT_DRIVER_FUNCTIONS) 1191 | 1192 | default: 1193 | reply_data_error_string(desc, cmd, false, Error::invalid_function); 1194 | return; 1195 | } 1196 | } 1197 | 1198 | // provide port driver data to erlang interface 1199 | 1200 | static ErlDrvEntry driver_entry_functions = { 1201 | // int(*init)(void) 1202 | // 1203 | // initialize global data 1204 | driver_entry_init, 1205 | // ErlDrvData(*start)(ErlDrvPort port, char *command) 1206 | // 1207 | // called when port is opened 1208 | driver_entry_start, 1209 | // void(*stop)(ErlDrvData drv_data) 1210 | // 1211 | // called when port is closed 1212 | driver_entry_stop, 1213 | // void(*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) 1214 | // 1215 | // called when erlang has sent 1216 | 0, 1217 | // void(*ready_input)(ErlDrvData drv_data, ErlDrvEvent event) 1218 | // 1219 | // called when input descriptor ready 1220 | 0, 1221 | // void(*ready_output)(ErlDrvData drv_data, ErlDrvEvent event) 1222 | // 1223 | // called when output descriptor ready 1224 | 0, 1225 | // char * driver_name 1226 | // 1227 | // the argument to open_port 1228 | const_cast(BOOST_PP_STRINGIZE(PORT_DRIVER_NAME)), 1229 | // void(*finish)(void) 1230 | // 1231 | // called when unloaded 1232 | 0, 1233 | // void * handle 1234 | // 1235 | // reserved -- used by emulator internally 1236 | 0, 1237 | // ErlDrvSSizeT(*control)(ErlDrvData drv_data, unsigned int command, 1238 | // char *buf, ErlDrvSizeT len, 1239 | // char **rbuf, ErlDrvSizeT rlen) 1240 | // 1241 | // "ioctl" for drivers - invoked by port_control/3 1242 | 0, 1243 | // void(*timeout)(ErlDrvData drv_data) 1244 | // 1245 | // handling of timeout in driver 1246 | 0, 1247 | // void(*outputv)(ErlDrvData drv_data, ErlIOVec *ev) 1248 | // 1249 | // called when we have output from erlang to the port instead of output 1250 | // if outputv is defined to handle vectorized erlang IO output (ErlIOVec) 1251 | driver_entry_outputv, 1252 | // void(*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data) 1253 | driver_entry_ready_async, 1254 | // void(*flush)(ErlDrvData drv_data) 1255 | // 1256 | // called when the port is about to be closed, and 1257 | // there is data in the driver queue that needs to be 1258 | // flushed before ’stop’ can be called 1259 | 0, 1260 | // ErlDrvSSizeT(*call)(ErlDrvData drv_data, unsigned int command, 1261 | // char *buf, ErlDrvSizeT len, char **rbuf, 1262 | // ErlDrvSizeT rlen, unsigned int *flags) 1263 | // 1264 | // Works mostly like ’control’, a synchronous call into the driver 1265 | 0, 1266 | // void(*event)(ErlDrvData drv_data, ErlDrvEvent event, 1267 | // ErlDrvEventData event_data) 1268 | // 1269 | // Called when an event selected by driver_event() has occurred 1270 | 0, 1271 | // extended_marker 1272 | static_cast(ERL_DRV_EXTENDED_MARKER), 1273 | // major_version 1274 | ERL_DRV_EXTENDED_MAJOR_VERSION, 1275 | // minor_version 1276 | ERL_DRV_EXTENDED_MINOR_VERSION, 1277 | // driver_flags 1278 | ERL_DRV_FLAG_USE_PORT_LOCKING, 1279 | // handle 1280 | // 1281 | // reserved -- used by emulator internally 1282 | 0, 1283 | // void(*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor) 1284 | // 1285 | // Called when a process monitor fires 1286 | 0, 1287 | // void(*stop_select)(ErlDrvEvent event, void *reserved) 1288 | // 1289 | // Called after a driver_select event object can be safely closed 1290 | 0 1291 | #ifdef ERLANG_19_SUPPORT 1292 | , 1293 | // void(*emergency_close)(ErlDrvData drv_data) 1294 | 0 1295 | #endif 1296 | }; 1297 | 1298 | extern "C" DRIVER_INIT(PORT_DRIVER_NAME) 1299 | { 1300 | return &driver_entry_functions; 1301 | } 1302 | 1303 | -------------------------------------------------------------------------------- /realloc_ptr.hpp: -------------------------------------------------------------------------------- 1 | //-*-Mode:C++;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | // ex: set ft=cpp fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | // 4 | // MIT License 5 | // 6 | // Copyright (c) 2009-2020 Michael Truog 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files (the "Software"), 10 | // to deal in the Software without restriction, including without limitation 11 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | // and/or sell copies of the Software, and to permit persons to whom the 13 | // Software is furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | // DEALINGS IN THE SOFTWARE. 25 | // 26 | #ifndef REALLOC_PTR_HPP 27 | #define REALLOC_PTR_HPP 28 | 29 | #include 30 | #include 31 | #include "assert.hpp" 32 | 33 | // the functionality of boost::scoped_array, however, 34 | // use malloc/realloc/free to resize the array by powers of two. 35 | // using realloc may be considered bad, but the hope is that the allocation 36 | // might be extended in memory, rather than a completely new allocation. 37 | // that is why the C++ new/delete are not used 38 | // (no C++ realloc exists, std::vector could be used instead but control 39 | // over the allocations would be lost). 40 | template 41 | class realloc_ptr 42 | { 43 | public: 44 | typedef T element_type; 45 | 46 | explicit realloc_ptr(size_t const size_initial, size_t const size_max) : 47 | m_size_initial(greater_pow2(size_initial)), 48 | m_size_max(greater_pow2(size_max)), 49 | m_size(m_size_initial), 50 | m_p(reinterpret_cast(::malloc(m_size_initial * sizeof(T)))) 51 | { 52 | } 53 | 54 | ~realloc_ptr() throw() 55 | { 56 | if (m_p) 57 | ::free(m_p); 58 | } 59 | 60 | T * release(bool const last = false) throw() 61 | { 62 | T * t = m_p; 63 | if (last) 64 | m_p = 0; 65 | else 66 | m_p = reinterpret_cast(::malloc(m_size_initial * sizeof(T))); 67 | return t; 68 | } 69 | 70 | size_t size() const 71 | { 72 | return m_size; 73 | } 74 | 75 | T & operator [](size_t const i) const 76 | { 77 | assert(i < m_size); 78 | return m_p[i]; 79 | } 80 | 81 | T * get() const 82 | { 83 | return m_p; 84 | } 85 | 86 | template 87 | R * get() const 88 | { 89 | assert(sizeof(R) == sizeof(T)); 90 | return reinterpret_cast(m_p); 91 | } 92 | 93 | bool copy(realloc_ptr & src, size_t const dst_i = 0) 94 | { 95 | assert(&src != this); 96 | if (! reserve(dst_i + src.m_size)) 97 | return false; 98 | ::memcpy(&(m_p[dst_i]), src.m_p, src.m_size * sizeof(T)); 99 | return true; 100 | } 101 | 102 | bool copy(realloc_ptr & src, 103 | size_t const src_n, size_t const dst_i) 104 | { 105 | assert(&src != this); 106 | if (! reserve(dst_i + src_n)) 107 | return false; 108 | ::memcpy(&(m_p[dst_i]), src.m_p, src_n * sizeof(T)); 109 | return true; 110 | } 111 | 112 | bool copy(realloc_ptr & src, 113 | size_t const src_i, size_t const src_n, size_t const dst_i) 114 | { 115 | assert(&src != this); 116 | if (! reserve(dst_i + src_n)) 117 | return false; 118 | ::memcpy(&(m_p[dst_i]), &(src.m_p[src_i]), src_n * sizeof(T)); 119 | return true; 120 | } 121 | 122 | bool move(size_t const src_i, size_t const src_n, size_t const dst_i) 123 | { 124 | if (! reserve(dst_i + src_n)) 125 | return false; 126 | ::memmove(&(m_p[dst_i]), &(m_p[src_i]), src_n * sizeof(T)); 127 | return true; 128 | } 129 | 130 | bool grow() 131 | { 132 | size_t const size_new = m_size << 1; 133 | if (size_new > m_size_max) 134 | return false; 135 | T * tmp = reinterpret_cast(::realloc(m_p, size_new * sizeof(T))); 136 | if (! tmp) 137 | return false; 138 | m_p = tmp; 139 | m_size = size_new; 140 | return true; 141 | } 142 | 143 | bool reserve(size_t const size) 144 | { 145 | if (size < m_size) 146 | return true; 147 | if (size > m_size_max) 148 | return false; 149 | size_t size_new = m_size; 150 | while (size >= size_new) 151 | size_new <<= 1; 152 | T * tmp = reinterpret_cast(::realloc(m_p, size_new * sizeof(T))); 153 | if (! tmp) 154 | return false; 155 | m_p = tmp; 156 | m_size = size_new; 157 | return true; 158 | } 159 | 160 | private: 161 | // find a value >= size_total as a power of 2 162 | size_t greater_pow2(size_t n) 163 | { 164 | size_t const size_total = n * sizeof(T); 165 | int bits = 0; 166 | for (size_t div2 = size_total; div2 > 1; div2 >>= 1) 167 | bits++; 168 | size_t const value = (1 << bits); 169 | if (value == size_total) 170 | return value; 171 | else 172 | return (value << 1); 173 | } 174 | 175 | size_t const m_size_initial; 176 | size_t const m_size_max; 177 | size_t m_size; 178 | T * m_p; 179 | 180 | realloc_ptr(realloc_ptr const &); 181 | realloc_ptr & operator =(realloc_ptr const &); 182 | 183 | }; 184 | 185 | #endif // REALLOC_PTR_HPP 186 | 187 | -------------------------------------------------------------------------------- /test_bindings.erl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | %%% 5 | %%% GENERIC ERLANG PORT [DRIVER] 6 | %%% automatically create Erlang bindings to C++/C that requires an OS process 7 | %%% 8 | %%% MIT License 9 | %%% 10 | %%% Copyright (c) 2009-2021 Michael Truog 11 | %%% 12 | %%% Permission is hereby granted, free of charge, to any person obtaining a 13 | %%% copy of this software and associated documentation files (the "Software"), 14 | %%% to deal in the Software without restriction, including without limitation 15 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 | %%% and/or sell copies of the Software, and to permit persons to whom the 17 | %%% Software is furnished to do so, subject to the following conditions: 18 | %%% 19 | %%% The above copyright notice and this permission notice shall be included in 20 | %%% all copies or substantial portions of the Software. 21 | %%% 22 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | %%% DEALINGS IN THE SOFTWARE. 29 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 30 | 31 | -module(test_bindings). 32 | 33 | -behavior(gen_server). 34 | 35 | %% external interface 36 | -export([test/0]). 37 | 38 | %% gen_server interface 39 | -export([start_link/0, start/0]). 40 | 41 | %% gen_server callbacks 42 | -export([init/1, 43 | handle_call/3, handle_cast/2, handle_info/2, 44 | terminate/2, code_change/3]). 45 | 46 | -include("erlang_functions.hrl"). 47 | 48 | % testing port driver by default, uncomment to test port 49 | -undef(ERL_PORT_DRIVER_NAME). 50 | 51 | -type client() :: {pid(), any()}. 52 | -record(state, 53 | { 54 | file_name :: string(), 55 | port :: port(), 56 | replies = [] :: list({non_neg_integer(), client()}) 57 | }). 58 | 59 | test() -> 60 | io:format("sync sleep~n", []), 61 | {ok, ok} = test_bindings:sleep_test1(?MODULE, 2), 62 | io:format("async sleep~n", []), 63 | case test_bindings:sleep_test2(?MODULE, 2) of 64 | ok -> ok; % async port driver call 65 | {ok, ok} -> ok % sync port call 66 | end, 67 | io:format("...~n", []), 68 | {ok, 18446744073709551615} = test_bindings:integer_test1(?MODULE), 69 | {ok, CharTest1} = test_bindings:char_test1(?MODULE, -1), 70 | true = (CharTest1 =:= -1) orelse (CharTest1 =:= 255), % standard ambiguous 71 | {ok, 255} = test_bindings:char_test2(?MODULE, -1), 72 | {ok, F1} = test_bindings:float_test1(?MODULE), 73 | true = F1 < 3.0e-8, 74 | {ok, "foobar is great"} = 75 | test_bindings:pchar_test1(?MODULE, "foobar is great"), 76 | {ok, "Mon Jan 18 19:14:07 2038\n"} = 77 | test_bindings:time_test1(?MODULE, 2147483647), 78 | {ok, "Sat Feb 6 22:28:15 2106\n"} = 79 | test_bindings:time_test1(?MODULE, 4294967295), 80 | {ok, F2} = test_bindings:float_test2(?MODULE, 0.3333333333333333), 81 | true = F2 > 0.3333333333333333, 82 | {ok, 377} = test_bindings:integer_test2(?MODULE, 55, 89, 377, -144), 83 | {ok, 610} = test_bindings:integer_test3(?MODULE, 34, 55, 144, 377), 84 | {ok, "abcdefghijklmnopqrstuvwxyz"} = 85 | test_bindings:pchar_test2(?MODULE, 86 | "abcdefgh", $i, 87 | "jklmnop", $q, 88 | "rstuvwxy", $z), 89 | {ok, <<"Hello World!">>} = test_bindings:hello_test1(?MODULE), 90 | {error, "Invalid function call"} = erroneous_call(?MODULE), 91 | ok. 92 | 93 | %%%------------------------------------------------------------------------ 94 | %%% Interface functions from gen_server 95 | %%%------------------------------------------------------------------------ 96 | 97 | start_link() -> 98 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 99 | 100 | start() -> 101 | gen_server:start({local, ?MODULE}, ?MODULE, [], []). 102 | 103 | %%%------------------------------------------------------------------------ 104 | %%% Callback functions from gen_server 105 | %%%------------------------------------------------------------------------ 106 | 107 | init([]) -> 108 | % port/port_driver find/load/open 109 | erlang:process_flag(trap_exit, true), 110 | Name = local_port_name(), 111 | case load_local_port(Name) of 112 | {ok, Port} when is_port(Port) -> 113 | {ok, #state{file_name = Name, 114 | port = Port}}; 115 | {error, Reason} -> 116 | {stop, Reason} 117 | end. 118 | 119 | %% handle synchronous function calls on the port/port_driver 120 | handle_call({call, Command, Message}, Client, 121 | #state{port = Port, 122 | replies = Replies} = State) -> 123 | case call_port(Port, Message) of 124 | ok -> 125 | {noreply, State#state{replies = Replies ++ [{Command, Client}]}}; 126 | {error, _} = Error -> 127 | {reply, Error, State} 128 | end; 129 | 130 | handle_call(Request, _, State) -> 131 | io:format("Unknown call \"~p\"~n", [Request]), 132 | {stop, "Unknown call", State}. 133 | 134 | %% handle asynchronous function calls on the port driver 135 | handle_cast({call, _, Message}, 136 | #state{port = Port} = State) -> 137 | case call_port(Port, Message) of 138 | ok -> 139 | ok; 140 | {error, Reason} -> 141 | io:format("error ~p~n", [Reason]), 142 | ok 143 | end, 144 | {noreply, State}; 145 | 146 | handle_cast(Request, State) -> 147 | io:format("Unknown cast \"~p\"~n", [Request]), 148 | {noreply, State}. 149 | 150 | %% port exited with a fatal error/signal 151 | handle_info({Port, {exit_status, Status}}, 152 | #state{port = Port} = State) -> 153 | {stop, "port exited with " ++ exit_status_to_string(Status), State}; 154 | 155 | %% port/port_driver sync response 156 | handle_info({Port, {data, Data}}, 157 | #state{port = Port} = State) -> 158 | case transform_data(Data) of 159 | {Command, Success} -> 160 | call_port_reply(Command, {ok, Success}, State); 161 | {error, 0, Reason} -> 162 | {stop, Reason, State}; 163 | {error, Command, Reason} -> 164 | call_port_reply(Command, {error, Reason}, State); 165 | {Stream, OsPid, Output} when Stream == stdout; Stream == stderr -> 166 | FormattedOutput = lists:flatmap(fun(Line) -> 167 | io_lib:format(" ~s~n", [Line]) 168 | end, string:tokens(Output, "\n")), 169 | io:format("~w (pid ~w):~n~s", [Stream, OsPid, FormattedOutput]), 170 | {noreply, State} 171 | end; 172 | 173 | %% port_driver async response 174 | handle_info({Port, {async, RawData}}, 175 | #state{port = Port} = State) -> 176 | Data = case transform_data(RawData) of 177 | {error, _, Reason} -> 178 | {error, Reason}; 179 | {_, Success} -> 180 | {ok, Success} 181 | end, 182 | io:format("async function call returned: ~p~n", [Data]), 183 | {noreply, State}; 184 | 185 | %% something unexpected happened when sending to the port 186 | handle_info({'EXIT', Port, PosixCode}, 187 | #state{port = Port} = State) -> 188 | {stop, port_exit_to_string(PosixCode), State}; 189 | 190 | handle_info(Request, State) -> 191 | io:format("Unknown info \"~p\"~n", [Request]), 192 | {noreply, State}. 193 | 194 | terminate(_, #state{port = Port}) -> 195 | _ = (catch erlang:port_close(Port)), 196 | ok; 197 | 198 | terminate(_, _) -> 199 | ok. 200 | 201 | code_change(_, State, _) -> 202 | {ok, State}. 203 | 204 | %%%------------------------------------------------------------------------ 205 | %%% Private functions 206 | %%%------------------------------------------------------------------------ 207 | 208 | -ifdef(ERL_PORT_DRIVER_NAME). 209 | local_port_name() -> 210 | ?ERL_PORT_DRIVER_NAME. 211 | %local_port_name_prefix() -> 212 | % ?ERL_PORT_DRIVER_NAME_PREFIX. 213 | %unload_local_port(Name) when is_list(Name) -> 214 | % % assume only one process owns the driver 215 | % % (otherwise more complex logic is necessary) 216 | % case erl_ddll:try_unload(Name, []) of 217 | % {ok, unloaded} -> 218 | % ok; 219 | % {ok, Error} when is_atom(Error) -> 220 | % {error, atom_to_list(Error)}; 221 | % {error, Error} when is_atom(Error) -> 222 | % {error, atom_to_list(Error)} 223 | % end. 224 | load_local_port(Name) when is_list(Name) -> 225 | io:format("using port driver~n", []), 226 | {ok, Path} = load_path(Name ++ ".so"), 227 | Result = case erl_ddll:load_driver(Path, Name) of 228 | ok -> 229 | ok; 230 | {error, already_loaded} -> 231 | ok; 232 | {error, ErrorDesc} -> 233 | {error, erl_ddll:format_error(ErrorDesc)} 234 | end, 235 | if 236 | Result =:= ok -> 237 | {ok, erlang:open_port({spawn, Name}, [])}; 238 | true -> 239 | Result 240 | end. 241 | transform_data(D) -> 242 | D. 243 | %% only a port driver can perform asynchronous function calls 244 | %% (you might be able to put a thread pool in an Erlang port, but why bother?) 245 | call_port_async(Process, Command, Message) 246 | when is_integer(Command), is_list(Message) -> 247 | gen_server:cast(Process, {call, Command, Message}). 248 | -else. 249 | -ifdef(ERL_PORT_NAME). 250 | local_port_name() -> 251 | ?ERL_PORT_NAME. 252 | %local_port_name_prefix() -> 253 | % ?ERL_PORT_NAME_PREFIX. 254 | %unload_local_port(Name) when is_list(Name) -> 255 | % ok. 256 | load_local_port(Name) when is_list(Name) -> 257 | io:format("using port~n", []), 258 | {ok, Path} = load_path(Name), 259 | P = erlang:open_port({spawn, Path ++ "/" ++ Name}, 260 | [{packet, 4}, binary, exit_status, nouse_stdio]), 261 | {ok, P}. 262 | transform_data(D) -> 263 | erlang:binary_to_term(D). 264 | call_port_async(Process, Command, Message) -> 265 | call_port_sync(Process, Command, Message). 266 | -endif. 267 | -endif. 268 | 269 | call_port_sync(Process, Command, Message) 270 | when is_integer(Command), is_list(Message) -> 271 | gen_server:call(Process, {call, Command, Message}). 272 | 273 | call_port(Port, Message) when is_port(Port), is_list(Message) -> 274 | try erlang:port_command(Port, Message) of 275 | true -> 276 | ok 277 | catch 278 | error:_ -> 279 | {error, badarg} 280 | end. 281 | 282 | call_port_reply(Command, Reply, 283 | #state{replies = Replies} = State) -> 284 | case lists:keytake(Command, 1, Replies) of 285 | false -> 286 | {stop, "invalid reply", State}; 287 | {value, {Command, Client}, RepliesNew} -> 288 | _ = gen_server:reply(Client, Reply), 289 | {noreply, State#state{replies = RepliesNew}} 290 | end. 291 | 292 | load_path(File) when is_list(File) -> 293 | Paths = lists:dropwhile(fun(D) -> 294 | case file:read_file_info(filename:join(D, File)) of 295 | {ok, _} -> false; 296 | _ -> true 297 | end 298 | end, code:get_path()), 299 | case Paths of 300 | [Dir | _] -> 301 | {ok, Dir}; 302 | [] -> 303 | {error, enoent} 304 | end. 305 | 306 | erroneous_call(Process) -> 307 | call_port_sync(Process, 32767, [<<32767:16/unsigned-integer-native>>]). 308 | 309 | port_exit_to_string(eacces) -> 310 | "permission denied"; 311 | port_exit_to_string(eagain) -> 312 | "resource temporarily unavailable"; 313 | port_exit_to_string(ebadf) -> 314 | "bad file number"; 315 | port_exit_to_string(ebusy) -> 316 | "file busy"; 317 | port_exit_to_string(edquot) -> 318 | "disk quota exceeded"; 319 | port_exit_to_string(eexist) -> 320 | "file already exists"; 321 | port_exit_to_string(efault) -> 322 | "bad address in system call argument"; 323 | port_exit_to_string(efbig) -> 324 | "file too large"; 325 | port_exit_to_string(eintr) -> 326 | "interrupted system call"; 327 | port_exit_to_string(einval) -> 328 | "invalid argument"; 329 | port_exit_to_string(eio) -> 330 | "IO error"; 331 | port_exit_to_string(eisdir) -> 332 | "illegal operation on a directory"; 333 | port_exit_to_string(eloop) -> 334 | "too many levels of symbolic links"; 335 | port_exit_to_string(emfile) -> 336 | "too many open files"; 337 | port_exit_to_string(emlink) -> 338 | "too many links"; 339 | port_exit_to_string(enametoolong) -> 340 | "file name too long"; 341 | port_exit_to_string(enfile) -> 342 | "file table overflow"; 343 | port_exit_to_string(enodev) -> 344 | "no such device"; 345 | port_exit_to_string(enoent) -> 346 | "no such file or directory"; 347 | port_exit_to_string(enomem) -> 348 | "not enough memory"; 349 | port_exit_to_string(enospc) -> 350 | "no space left on device"; 351 | port_exit_to_string(enotblk) -> 352 | "block device required"; 353 | port_exit_to_string(enotdir) -> 354 | "not a directory"; 355 | port_exit_to_string(enotsup) -> 356 | "operation not supported"; 357 | port_exit_to_string(enxio) -> 358 | "no such device or address"; 359 | port_exit_to_string(eperm) -> 360 | "not owner"; 361 | port_exit_to_string(epipe) -> 362 | "broken pipe"; 363 | port_exit_to_string(erofs) -> 364 | "read-only file system"; 365 | port_exit_to_string(espipe) -> 366 | "invalid seek"; 367 | port_exit_to_string(esrch) -> 368 | "no such process"; 369 | port_exit_to_string(estale) -> 370 | "stale remote file handle"; 371 | port_exit_to_string(exdev) -> 372 | "cross-domain link"; 373 | port_exit_to_string(Other) when is_atom(Other) -> 374 | erlang:atom_to_list(Other). 375 | 376 | %% exit status messages 377 | 378 | exit_status_to_string( 0) -> "no error occurred"; 379 | %% GEPD exit status values (from InternalExitStatus in port.cpp) 380 | %% exit_status >= GEPD::ExitStatus::errors_min (from port.hpp) 381 | %% (GEPD::ExitStatus::errors_min == 80) 382 | exit_status_to_string( 80) -> "erlang exited"; 383 | exit_status_to_string( 81) -> "erlang port read EAGAIN"; 384 | exit_status_to_string( 82) -> "erlang port read EBADF"; 385 | exit_status_to_string( 83) -> "erlang port read EFAULT"; 386 | exit_status_to_string( 84) -> "erlang port read EINTR"; 387 | exit_status_to_string( 85) -> "erlang port read EINVAL"; 388 | exit_status_to_string( 86) -> "erlang port read EIO"; 389 | exit_status_to_string( 87) -> "erlang port read EISDIR"; 390 | exit_status_to_string( 88) -> "erlang port read null"; 391 | exit_status_to_string( 89) -> "erlang port read overflow"; 392 | exit_status_to_string( 90) -> "erlang port read unknown"; 393 | exit_status_to_string( 91) -> "erlang port write EAGAIN"; 394 | exit_status_to_string( 92) -> "erlang port write EBADF"; 395 | exit_status_to_string( 93) -> "erlang port write EFAULT"; 396 | exit_status_to_string( 94) -> "erlang port write EFBIG"; 397 | exit_status_to_string( 95) -> "erlang port write EINTR"; 398 | exit_status_to_string( 96) -> "erlang port write EINVAL"; 399 | exit_status_to_string( 97) -> "erlang port write EIO"; 400 | exit_status_to_string( 98) -> "erlang port write ENOSPC"; 401 | exit_status_to_string( 99) -> "erlang port write EPIPE"; 402 | exit_status_to_string(100) -> "erlang port write null"; 403 | exit_status_to_string(101) -> "erlang port write overflow"; 404 | exit_status_to_string(102) -> "erlang port write unknown"; 405 | exit_status_to_string(103) -> "erlang port ei_encode_error"; 406 | exit_status_to_string(104) -> "erlang port poll EBADF"; 407 | exit_status_to_string(105) -> "erlang port poll EFAULT"; 408 | exit_status_to_string(106) -> "erlang port poll EINTR"; 409 | exit_status_to_string(107) -> "erlang port poll EINVAL"; 410 | exit_status_to_string(108) -> "erlang port poll ENOMEM"; 411 | exit_status_to_string(109) -> "erlang port poll ERR"; 412 | exit_status_to_string(110) -> "erlang port poll HUP"; 413 | exit_status_to_string(111) -> "erlang port poll NVAL"; 414 | exit_status_to_string(112) -> "erlang port poll unknown"; 415 | exit_status_to_string(113) -> "erlang port pipe EFAULT"; 416 | exit_status_to_string(114) -> "erlang port pipe EINVAL"; 417 | exit_status_to_string(115) -> "erlang port pipe EMFILE"; 418 | exit_status_to_string(116) -> "erlang port pipe ENFILE"; 419 | exit_status_to_string(117) -> "erlang port pipe unknown"; 420 | exit_status_to_string(118) -> "erlang port dup EBADF"; 421 | exit_status_to_string(119) -> "erlang port dup EBUSY"; 422 | exit_status_to_string(120) -> "erlang port dup EINTR"; 423 | exit_status_to_string(121) -> "erlang port dup EINVAL"; 424 | exit_status_to_string(122) -> "erlang port dup EMFILE"; 425 | exit_status_to_string(123) -> "erlang port dup unknown"; 426 | exit_status_to_string(124) -> "erlang port close EBADF"; 427 | exit_status_to_string(125) -> "erlang port close EINTR"; 428 | exit_status_to_string(126) -> "erlang port close EIO"; 429 | exit_status_to_string(127) -> "erlang port close unknown"; 430 | exit_status_to_string(Status) when is_integer(Status) -> 431 | if 432 | Status > 128 -> 433 | %% exit status values due to signals 434 | signal_to_string(Status - 128); 435 | true -> 436 | erlang:integer_to_list(Status) 437 | end. 438 | 439 | % Only signal integers that are consistent among all platforms use a 440 | % specific string. 441 | signal_to_string( 1) -> "SIGHUP"; 442 | signal_to_string( 2) -> "SIGINT"; 443 | signal_to_string( 3) -> "SIGQUIT"; 444 | signal_to_string( 4) -> "SIGILL"; 445 | signal_to_string( 5) -> "SIGTRAP"; 446 | signal_to_string( 6) -> "SIGABRT"; 447 | signal_to_string( 8) -> "SIGFPE"; 448 | signal_to_string( 9) -> "SIGKILL"; 449 | signal_to_string(11) -> "SIGSEGV"; 450 | signal_to_string(13) -> "SIGPIPE"; 451 | signal_to_string(14) -> "SIGALRM"; 452 | signal_to_string(15) -> "SIGTERM"; 453 | signal_to_string(Signal) when is_integer(Signal), Signal > 0 -> 454 | "SIG#" ++ erlang:integer_to_list(Signal). 455 | 456 | -------------------------------------------------------------------------------- /test_bindings.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_BINDINGS_H 2 | #define TEST_BINDINGS_H 3 | 4 | #if ! defined(CURRENT_VERSION) 5 | #error CURRENT_VERSION is required as part of the port/port_driver name \ 6 | for code upgrades/downgrades 7 | #endif 8 | 9 | // SUPPORTED FUNCTION ARGUMENT TYPES: 10 | // int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t, 11 | // bool, char, uchar, double, time_t, pchar_len, puint32_len 12 | // (pchar_len represents two function arguments: char* and uint32_t 13 | // puint32_len represents two function arguments: uint32_t* and uint32_t 14 | // function argument memory is managed externally and should never be freed) 15 | 16 | // SUPPORTED FUNCTION RETURN VALUE TYPES: 17 | // int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t, 18 | // bool, char, uchar, double, float, time_t, void, 19 | // pchar, pchar_free, pchar_nofree, 20 | // pchar_len_t, pchar_len_t_free, pchar_len_t_nofree 21 | // (pchar == pchar_nofree, pchar_len_t == pchar_len_t_free, 22 | // pchar returns an Erlang string, i.e., list of integers, 23 | // pchar_len_t return an Erlang binary) 24 | 25 | ////////////////////////////////////////////////////////////////////////////// 26 | // Port Driver Declaration 27 | ////////////////////////////////////////////////////////////////////////////// 28 | 29 | // specify the name of the driver, as provided to driver initialization 30 | // (e.g., erl_ddll:load_driver/2, erlang:open_port/2, ErlDrvEntry, etc.) 31 | #define PORT_DRIVER_NAME_PREFIX test_functions_port_driver 32 | 33 | // specify the C or C++ include file with the functions that will be called 34 | // from within the Erlang code 35 | #define PORT_DRIVER_C_FUNCTIONS_HEADER_FILE "test_functions.h" 36 | //#define PORT_DRIVER_CXX_FUNCTIONS_HEADER_FILE "test_functions.h" 37 | 38 | // specify all the functions to generate bindings for 39 | // __________________________________________________________________________ 40 | // || FUNCTION || ARITY/TYPES || RETURN TYPE || ASYNC CALL || 41 | #define PORT_DRIVER_FUNCTIONS \ 42 | ((sleep_test1, 1, (uint32_t), void, 0)) \ 43 | ((sleep_test2, 1, (uint32_t), void, 1)) \ 44 | ((integer_test1, 0, (), uint64_t, 0)) \ 45 | ((char_test1, 1, (char), char, 0)) \ 46 | ((char_test2, 1, (uchar), uchar, 0)) \ 47 | ((float_test1, 0, (), float, 0)) \ 48 | ((pchar_test1, 1, (pchar_len), pchar_nofree, 0)) \ 49 | ((time_test1, 1, (time_t), pchar_nofree, 0)) \ 50 | ((float_test2, 1, (double), float, 0)) \ 51 | ((integer_test2, 4, (int8_t, int16_t, \ 52 | int32_t, int64_t), int32_t, 0)) \ 53 | ((integer_test3, 4, (uint8_t, uint16_t, \ 54 | uint32_t, uint64_t), uint32_t, 0)) \ 55 | ((pchar_test2, 6, (pchar_len, char, \ 56 | pchar_len, char, \ 57 | pchar_len, char), pchar_nofree, 0)) \ 58 | ((hello_test1, 0, (), pchar_len_t_free, 0)) 59 | 60 | ////////////////////////////////////////////////////////////////////////////// 61 | // Port Declaration 62 | ////////////////////////////////////////////////////////////////////////////// 63 | 64 | // specify the name of the port, as provided to port initialization 65 | // (e.g., erlang:open_port/2, executable name) 66 | #define PORT_NAME_PREFIX test_functions_port 67 | 68 | // specify the C or C++ include file with the functions that will be called 69 | // from within the Erlang code 70 | #define PORT_C_FUNCTIONS_HEADER_FILE "test_functions.h" 71 | //#define PORT_CXX_FUNCTIONS_HEADER_FILE "test_functions.h" 72 | 73 | // not necessary if PORT_DRIVER_FUNCTIONS is defined 74 | /* 75 | #define PORT_FUNCTIONS \ 76 | ((sleep_test1, 1, (uint32_t), void )) \ 77 | ((sleep_test2, 1, (uint32_t), void )) \ 78 | ((integer_test1, 0, (), uint64_t)) \ 79 | ((char_test1, 1, (char), char )) \ 80 | ((char_test2, 1, (uchar), uchar )) \ 81 | ((float_test1, 0, (), float )) \ 82 | ((pchar_test1, 1, (pchar_len), pchar )) \ 83 | ((time_test1, 1, (time_t), pchar )) \ 84 | ((float_test2, 1, (double), float )) \ 85 | ((integer_test2, 4, (int8_t,int16_t,int32_t,int64_t), int32_t )) \ 86 | ((integer_test3, 4, (uint8_t,uint16_t,uint32_t,uint64_t),uint32_t)) \ 87 | ((pchar_test2, 6, (pchar_len, char, pchar_len, char, \ 88 | pchar_len, char), pchar)) 89 | */ 90 | 91 | ////////////////////////////////////////////////////////////////////////////// 92 | 93 | #include 94 | 95 | #if defined(PORT_DRIVER_NAME_PREFIX) 96 | #define PORT_DRIVER_NAME \ 97 | BOOST_PP_CAT(BOOST_PP_CAT(PORT_DRIVER_NAME_PREFIX, _), CURRENT_VERSION) 98 | #endif 99 | #if defined(PORT_NAME_PREFIX) 100 | #define PORT_NAME \ 101 | BOOST_PP_CAT(BOOST_PP_CAT(PORT_NAME_PREFIX, _), CURRENT_VERSION) 102 | #endif 103 | 104 | 105 | #endif // TEST_BINDINGS_H 106 | -------------------------------------------------------------------------------- /test_functions.c: -------------------------------------------------------------------------------- 1 | #include "test_functions.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // test asynchronous/synchronous call mechanism (and a void return value) 11 | void sleep_test1(uint32_t arg) 12 | { 13 | sleep(arg); 14 | } 15 | 16 | void sleep_test2(uint32_t arg) 17 | { 18 | fprintf(stdout, "stdout writing before %d second sleep\n", arg); 19 | sleep(arg); 20 | fprintf(stdout, "stdout writing after %d second sleep\n", arg); 21 | fprintf(stderr, "stderr\nline\nbreak(s)\nmissing"); 22 | } 23 | 24 | uint64_t integer_test1() 25 | { 26 | return 0xffffffffffffffff; 27 | } 28 | 29 | char char_test1(char a) 30 | { 31 | return a; 32 | } 33 | 34 | unsigned char char_test2(unsigned char a) 35 | { 36 | return a; 37 | } 38 | 39 | float float_test1() 40 | { 41 | const float x = 1.0f / 3.0f; 42 | return (x * 3.0 - 1.0); 43 | } 44 | 45 | static char pchar_test1_data[128]; 46 | char * pchar_test1(char * p, uint32_t length) 47 | { 48 | memcpy(pchar_test1_data, p, length); 49 | pchar_test1_data[length] = '\0'; 50 | return pchar_test1_data; 51 | } 52 | 53 | static char time_test1_data[128]; 54 | char * time_test1(time_t t) 55 | { 56 | return ctime_r(&t, time_test1_data); 57 | } 58 | 59 | float float_test2(double value) 60 | { 61 | return (float) value; 62 | } 63 | 64 | int32_t integer_test2(int8_t size1, int16_t size2, 65 | int32_t size4, int64_t size8) 66 | { 67 | return size1 + size2 + size4 + 68 | ((size8 & 0x7fffffff) | ((size8 & 0x8000000000000000) >> 32)); 69 | } 70 | 71 | uint32_t integer_test3(uint8_t size1, uint16_t size2, 72 | uint32_t size4, uint64_t size8) 73 | { 74 | return size1 + size2 + size4 + (size8 & 0xffffffff); 75 | } 76 | 77 | static char pchar_test2_data[128]; 78 | char * pchar_test2(char * p1, uint32_t l1, char c1, 79 | char * p2, uint32_t l2, char c2, 80 | char * p3, uint32_t l3, char c3) 81 | { 82 | memcpy(&pchar_test2_data[0], p1, l1); 83 | pchar_test2_data[l1] = c1; 84 | memcpy(&pchar_test2_data[l1 + 1], p2, l2); 85 | pchar_test2_data[l1 + l2 + 1] = c2; 86 | memcpy(&pchar_test2_data[l1 + l2 + 2], p3, l3); 87 | pchar_test2_data[l1 + l2 + l3 + 2] = c3; 88 | pchar_test2_data[l1 + l2 + l3 + 3] = '\0'; 89 | return pchar_test2_data; 90 | } 91 | 92 | pchar_len_t hello_test1() 93 | { 94 | pchar_len_t hello; 95 | char const * message = "Hello World!"; 96 | /* do not include a '\0' character, 97 | * Erlang detects it is a binary string with magic! 98 | */ 99 | hello.pchar = malloc(12); 100 | memcpy(hello.pchar, message, 12); 101 | hello.length = 12; 102 | return hello; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /test_functions.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_FUNCTIONS_H 2 | #define TEST_FUNCTIONS_H 3 | 4 | #include 5 | #include 6 | #include "pchar_len_t.h" 7 | 8 | void sleep_test1(uint32_t arg); 9 | void sleep_test2(uint32_t arg); 10 | uint64_t integer_test1(); 11 | char char_test1(char a); 12 | unsigned char char_test2(unsigned char a); 13 | float float_test1(); 14 | char * pchar_test1(char * p, uint32_t length); 15 | char * time_test1(time_t t); 16 | 17 | 18 | float float_test2(double value); 19 | int32_t integer_test2(int8_t size1, int16_t size2, 20 | int32_t size4, int64_t size8); 21 | uint32_t integer_test3(uint8_t size1, uint16_t size2, 22 | uint32_t size4, uint64_t size8); 23 | char * pchar_test2(char * p1, uint32_t l1, char c1, 24 | char * p2, uint32_t l2, char c2, 25 | char * p3, uint32_t l3, char c3); 26 | pchar_len_t hello_test1(); 27 | 28 | #endif // TEST_FUNCTIONS_H 29 | --------------------------------------------------------------------------------