Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ QualifierOrder: [
'const',
'type',
]
SpaceInEmptyBraces: Block
2 changes: 1 addition & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Checks: >
-*,
clang-diagnostic-*,
clang-analyzer-optin.*,
llvm-*,-llvm-header-guard,
llvm-*,-llvm-header-guard,-llvm-prefer-static-over-anonymous-namespace,
google-*,-google-readability-todo,-google-readability-braces-around-statements,
hicpp-*,-hicpp-braces-around-statements,-hicpp-use-emplace,-hicpp-named-parameter,
-hicpp-no-array-decay,-hicpp-uppercase-literal-suffix,
Expand Down
14 changes: 11 additions & 3 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,12 @@ jobs:
gtest-dev
- name: Fixup environment
run: |
# add missing final symlink for Lua library
ln -s liblua-5.2.so.0 /usr/lib/liblua-5.2.so
# use ninja-build as ninja binary (alternative: install samurai instead)
ln -s /usr/lib/ninja-build/bin/ninja /usr/bin/ninja
# patch Boost.process to not use ::close_range() since Alpine's musl implementation does not provide it
sed -i 's/#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1/\/\/#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1\n#include <dirent.h>/g' /usr/include/boost/process/v2/posix/detail/close_handles.ipp
shell: alpine.sh --root {0}

- name: Prepare
Expand Down Expand Up @@ -92,7 +96,7 @@ jobs:
retention-days: 5

unittest:
name: "unittest"
name: "unittests"
needs: build
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -151,7 +155,7 @@ jobs:
retention-days: 3

ctest:
name: "ctest"
name: "cmake tests"
needs: build
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -179,8 +183,12 @@ jobs:
gtest-dev
- name: Fixup environment
run: |
ln -s /usr/lib/ninja-build/bin/ninja /usr/bin/ninja
# add missing final symlink for Lua library
ln -s liblua-5.2.so.0 /usr/lib/liblua-5.2.so
# use ninja-build as ninja binary (alternative: install samurai instead)
ln -s /usr/lib/ninja-build/bin/ninja /usr/bin/ninja
# patch Boost.process to not use ::close_range() since Alpine's musl implementation does not provide it
sed -i 's/#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1/\/\/#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1\n#include <dirent.h>/g' /usr/include/boost/process/v2/posix/detail/close_handles.ipp
shell: alpine.sh --root {0}

- name: Run cmake tests
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/cpp-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ jobs:
clang19-extra-tools
- name: Fixup environment
run: |
# add missing final symlink for Lua library
ln -s liblua-5.2.so.0 /usr/lib/liblua-5.2.so
# use ninja-build as ninja binary (alternative: install samurai instead)
ln -s /usr/lib/ninja-build/bin/ninja /usr/bin/ninja
# patch Boost.process to not use ::close_range() since Alpine's musl implementation does not provide it
sed -i 's/#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1/\/\/#define BOOST_PROCESS_V2_HAS_CLOSE_RANGE 1\n#include <dirent.h>/g' /usr/include/boost/process/v2/posix/detail/close_handles.ipp
shell: alpine.sh --root {0}

- name: Generate compile_commands.json
Expand Down
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ option(PPPLUGIN_ENABLE_UNDEFINED_SANITIZE
option(PPPLUGIN_ENABLE_UNREACHABLE_SANITIZE
"Enable compilation with unreachable sanitize flags" OFF)

# required Boost header-only components: dll, algorithm, asio, process, uuid;
# Boost.filesystem is only required because of Boost.dll
find_package(Boost 1.70.0 REQUIRED CONFIG COMPONENTS headers filesystem)
if(${Boost_VERSION} VERSION_GREATER_EQUAL 1.85.0)
# Boost.process was header-only until 1.84; requires linking in >= 1.85
find_package(Boost REQUIRED CONFIG COMPONENTS process)
else()
# add dummy target so that this check is only necessary once
add_library(Boost::process ALIAS Boost::headers)
endif()
find_package(Python 3.0 REQUIRED COMPONENTS Development)
find_package(Lua 5.2 REQUIRED)

Expand Down
12 changes: 9 additions & 3 deletions cmake/ppplugin-config.cmake.in
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
@PACKAGE_INIT@

include(CMakeFindDependencyMacro)
find_dependency(Boost @Boost_VERSION_MAJOR@ COMPONENTS headers filesystem
python)
find_dependency(Boost @Boost_VERSION_MAJOR@ CONFIG COMPONENTS headers filesystem)
if(@Boost_VERSION@ VERSION_GREATER_EQUAL 1.85.0)
# Boost.process was header-only until 1.84; requires linking in >= 1.85
find_dependency(Boost CONFIG COMPONENTS process)
else()
# add dummy target so that this check is only necessary once
add_library(Boost::process ALIAS Boost::headers)
endif()
find_dependency(Python @Python_VERSION_MAJOR@ COMPONENTS Development)
find_dependency(Lua @LUA_VERSION_MAJOR@)

if(${PPPLUGIN_ENABLE_CPP17_COMPATIBILITY})
if(@PPPLUGIN_ENABLE_CPP17_COMPATIBILITY@)
find_dependency(fmt @fmt_VERSION_MAJOR@)
endif()

Expand Down
39 changes: 39 additions & 0 deletions include/ppplugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// this source file includes all header files of the library;
// it allows the language server to infer the correct compilation flags for header
// files that do not have a corresponding source file; additionally, this will
// cause the compiler to compile check them in the compilation process, even if
// they are not used elsewhere in the library (external interface)

#include "ppplugin/errors.h"
#include "ppplugin/expected.h"
#include "ppplugin/noop_plugin.h"
#include "ppplugin/plugin.h"
#include "ppplugin/plugin_manager.h"

#include "ppplugin/c/plugin.h"

#include "ppplugin/cpp/plugin.h"

#include "ppplugin/lua/lua_helpers.h"
#include "ppplugin/lua/lua_script.h"
#include "ppplugin/lua/lua_state.h"
#include "ppplugin/lua/plugin.h"

#include "ppplugin/shell/plugin.h"
#include "ppplugin/shell/shell_session.h"

#include "ppplugin/python/plugin.h"
#include "ppplugin/python/python_exception.h"
#include "ppplugin/python/python_forward_defs.h"
#include "ppplugin/python/python_guard.h"
#include "ppplugin/python/python_interpreter.h"
#include "ppplugin/python/python_object.h"
#include "ppplugin/python/python_tuple.h"

#include "ppplugin/detail/boost_dll_loader.h"
#include "ppplugin/detail/compatibility_utils.h"
#include "ppplugin/detail/compiler_info.h"
#include "ppplugin/detail/function_details.h"
#include "ppplugin/detail/scope_guard.h"
#include "ppplugin/detail/string_utils.h"
#include "ppplugin/detail/template_helpers.h"
20 changes: 12 additions & 8 deletions include/ppplugin/c/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,26 @@ CallResult<ReturnValue> CPlugin::call(const std::string& function_name, Args&&..
template <typename VariableType>
CallResult<VariableType> CPlugin::global(const std::string& variable_name)
{
auto p = detail::boost_dll::getSymbol(plugin_, variable_name);
if (p.hasValue()) {
return *reinterpret_cast<VariableType*>(p.value().value());
auto result_pointer = detail::boost_dll::getSymbol(plugin_, variable_name);
if (result_pointer.hasValue()) {
// raw type casting necessary due to lack of type information in shared library
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return *reinterpret_cast<VariableType*>(result_pointer.value().value());
}
return { p.error().value() };
return { result_pointer.error().value() };
}

template <typename VariableType>
CallResult<void> CPlugin::global(const std::string& variable_name, VariableType&& new_value)
{
auto p = detail::boost_dll::getSymbol(plugin_, variable_name);
if (p.hasValue()) {
*reinterpret_cast<VariableType*>(p.value().value()) = std::forward<VariableType>(new_value);
auto result_pointer = detail::boost_dll::getSymbol(plugin_, variable_name);
if (result_pointer.hasValue()) {
// raw type casting necessary due to lack of type information in shared library
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
*reinterpret_cast<VariableType*>(result_pointer.value().value()) = std::forward<VariableType>(new_value);
return {};
}
return { p.error().value() };
return { result_pointer.error().value() };
}
} // namespace ppplugin

Expand Down
20 changes: 12 additions & 8 deletions include/ppplugin/cpp/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,26 @@ CallResult<ReturnValue> CppPlugin::call(const std::string& function_name, Args&&
template <typename VariableType>
CallResult<VariableType> CppPlugin::global(const std::string& variable_name)
{
auto p = detail::boost_dll::getSymbol(plugin_, variable_name);
if (p.hasValue()) {
return *reinterpret_cast<VariableType*>(p.value().value());
auto result_pointer = detail::boost_dll::getSymbol(plugin_, variable_name);
if (result_pointer.hasValue()) {
// raw type casting necessary due to lack of type information in shared library
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return *reinterpret_cast<VariableType*>(result_pointer.value().value());
}
return { p.error().value() };
return { result_pointer.error().value() };
}

template <typename VariableType>
CallResult<void> CppPlugin::global(const std::string& variable_name, VariableType&& new_value)
{
auto p = detail::boost_dll::getSymbol(plugin_, variable_name);
if (p.hasValue()) {
*reinterpret_cast<VariableType*>(p.value().value()) = std::forward<VariableType>(new_value);
auto result_pointer = detail::boost_dll::getSymbol(plugin_, variable_name);
if (result_pointer.hasValue()) {
// raw type casting necessary due to lack of type information in shared library
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
*reinterpret_cast<VariableType*>(result_pointer.value().value()) = std::forward<VariableType>(new_value);
return {};
}
return { p.error().value() };
return { result_pointer.error().value() };
}
} // namespace ppplugin

Expand Down
5 changes: 2 additions & 3 deletions include/ppplugin/detail/scope_guard.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class ScopeGuard final {
template <typename Func,
typename = std::enable_if_t<!std::is_same_v<Func, ScopeGuard>>>
explicit ScopeGuard(Func&& func)
: function_ { func }
: function_ { std::forward<Func>(func) }
{
}
~ScopeGuard() { call(); }
Expand Down Expand Up @@ -40,8 +40,7 @@ class ScopeGuard final {
void cancel() { function_ = {}; }

private:
std::function<void()>
function_;
std::function<void()> function_;
};
} // namespace ppplugin::detail

Expand Down
36 changes: 36 additions & 0 deletions include/ppplugin/detail/string_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef PPPLUGIN_DETAIL_STRING_UTILS_H
#define PPPLUGIN_DETAIL_STRING_UTILS_H

#include "template_helpers.h"

#include <charconv>
#include <optional>
#include <string_view>

namespace ppplugin::detail {
template <typename T>
constexpr auto IsStringlikeV = // NOLINT(readability-identifier-naming)
templates::IsAnyOfV<T, std::string, std::string_view, const char*>;

[[nodiscard]] inline bool endsWith(std::string_view string, std::string_view end)
{
if (string.size() < end.size()) {
return false;
}
auto result = string.compare(string.size() - end.size(), end.size(), end);
return result == 0;
}

template <typename Integer>
[[nodiscard]] inline std::optional<Integer> toInteger(std::string_view string)
{
int value {};
auto result = std::from_chars(string.begin(), string.end(), value);
if (result.ec == std::errc {} && result.ptr == string.end()) {
return value;
}
return std::nullopt;
}
} // namespace ppplugin::detail

#endif // PPPLUGIN_DETAIL_STRING_UTILS_H
10 changes: 10 additions & 0 deletions include/ppplugin/detail/template_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,23 @@ struct IsSpecialization : std::false_type { };
template <template <typename...> typename ExpectedType, typename... Ts>
struct IsSpecialization<ExpectedType<Ts...>, ExpectedType> : std::true_type { };

/**
* Check if first type is template instantiation of second type.
*/
template <typename ActualType, template <typename...> typename ExpectedType>
constexpr bool IsSpecializationV = // NOLINT(readability-identifier-naming)
IsSpecialization<ActualType, ExpectedType>::value;

template <typename T>
using IsStdTuple = IsSpecialization<T, std::tuple>;

/**
* Check if first type is any of the following types.
*/
template <typename ActualType, typename... Types>
constexpr bool IsAnyOfV = // NOLINT(readability-identifier-naming)
(std::is_same_v<ActualType, Types> || ...);

/**
* C++17 compatible version of std::remove_cvref_t.
*/
Expand Down
5 changes: 3 additions & 2 deletions include/ppplugin/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "ppplugin/detail/compatibility_utils.h"
#include "ppplugin/expected.h"

#include <cstdint>
#ifndef PPPLUGIN_CPP17_COMPATIBILITY
#include <source_location>
#endif // PPPLUGIN_CPP17_COMPATIBILITY
Expand All @@ -13,7 +14,7 @@
namespace ppplugin {
class CallError {
public:
enum class Code {
enum class Code : std::uint8_t {
unknown,
notLoaded,
symbolNotFound,
Expand Down Expand Up @@ -92,7 +93,7 @@ class CallError {
}
}

enum class LoadError {
enum class LoadError : std::uint8_t {
unknown,
fileNotFound,
fileInvalid,
Expand Down
36 changes: 30 additions & 6 deletions include/ppplugin/expected.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,17 +459,33 @@ template <typename F>
constexpr auto Expected<T, E>::andThen(F&& func) const&
{
if constexpr (std::is_invocable_v<F, T>) {
using ReturnType = AndThenReturnType<std::invoke_result_t<F, T>>;
using InvokeResultType = std::invoke_result_t<F, T>;
using ReturnType = AndThenReturnType<InvokeResultType>;
if (!hasValue()) {
return static_cast<ReturnType>(uncheckedError());
}
return static_cast<ReturnType>(std::forward<F>(func)(uncheckedValue()));
if constexpr (std::is_void_v<InvokeResultType>) {
// cannot cast void function return type to result type;
// thus, handle separately here
std::forward<F>(func)(uncheckedValue());
return ReturnType {};
} else {
return static_cast<ReturnType>(std::forward<F>(func)(uncheckedValue()));
}
} else if constexpr (std::is_invocable_v<F>) {
using ReturnType = AndThenReturnType<std::invoke_result_t<F>>;
using InvokeResultType = std::invoke_result_t<F>;
using ReturnType = AndThenReturnType<InvokeResultType>;
if (!hasValue()) {
return static_cast<ReturnType>(uncheckedError());
}
return static_cast<ReturnType>(std::forward<F>(func)());
if constexpr (std::is_void_v<InvokeResultType>) {
// cannot cast void function return type to result type;
// thus, handle separately here
std::forward<F>(func)();
return ReturnType {};
} else {
return static_cast<ReturnType>(std::forward<F>(func)());
}
} else {
static_assert(std::is_invocable_v<F>,
"Given function has to be invocable!");
Expand Down Expand Up @@ -658,10 +674,18 @@ template <typename E>
template <typename F>
constexpr auto Expected<void, E>::andThen(F&& func) &&
{
using FunctionReturnType = std::invoke_result_t<F>;
using ReturnType = Expected<FunctionReturnType, E>;

if (error_) {
return std::move(*error_);
return ReturnType { std::move(*error_) };
}
if constexpr (std::is_void_v<FunctionReturnType>) {
func();
return ReturnType {};
} else {
return ReturnType { std::forward<F>(func)() };
}
return std::forward<F>(func)();
}
template <typename E>
constexpr void Expected<void, E>::operator*() const
Expand Down
Loading
Loading