Compare commits

...

10 Commits

Author SHA1 Message Date
Bart Beumer 37dcda3d96 Merge pull request 'develop-libmagic' (#10) from develop-libmagic into master 2025-04-13 19:16:50 +00:00
Bart Beumer 008102df56 Introduce libmagic. 2025-04-13 21:10:56 +02:00
Bart Beumer fb1bfe5070 Introduce Dev Container.
This was introduced because some conan package could not handle the very
new version of CMake that was installed on my local machine even if I
selected the latest version.
2025-04-13 19:33:35 +02:00
Bart Beumer bc63cfa92e Merge pull request 'Fixed Clang 17.0.2 not being able to build the code.' (#9) from develop-update into master
Reviewed-on: https://git.4beumer.nl/bart/network-experiment/pulls/9
2025-01-30 22:19:51 +00:00
Bart Beumer bf09edcd63 Fixed Clang 17.0.2 not being able to build the code. 2025-01-30 23:14:04 +01:00
Bart Beumer 1b67032ee6 Merge pull request 'Update required CMake version. use conan.' (#8) from develop-update into master
Reviewed-on: https://git.4beumer.nl/bart/network-experiment/pulls/8
2025-01-30 22:01:43 +00:00
Bart Beumer 692678099c Update required CMake version. use conan. 2025-01-30 22:42:01 +01:00
Bart Beumer 14dec24c54 Merge pull request 'feature/bugfix-argbyref' (#3) from feature/bugfix-argbyref into master
Reviewed-on: https://git.4beumer.nl/bart/network-experiment/pulls/3
2023-10-03 18:55:08 +00:00
Bart Beumer beced114ae Fix function_wrapper extra parameter can be by reference. 2023-10-03 20:49:29 +02:00
Bart Beumer c5be670806 Extend flexible_value variant with vector of itself. 2023-10-03 20:39:13 +02:00
21 changed files with 307 additions and 87 deletions

22
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,22 @@
FROM alpine:3.21.3
# Install tools required for building.
RUN apk update && \
apk add --no-cache \
autoconf \
bash \
boost-build \
build-base \
cmake \
gdb \
git \
libstdc++ \
libtool \
linux-headers \
ninja \
m4 \
perl \
python3 \
py3-pip && \
pip install --break-system-packages conan && \
conan profile detect

View File

@ -0,0 +1,19 @@
{
"name": "C++",
"build": {
"dockerfile": "Dockerfile"
},
// Configure tool-specific properties.
"customizations": {
"vscode": {
"settings": {},
"extensions": [
"ms-vscode.cpptools",
"twxs.cmake",
"ms-vscode.cmake-tools",
"konicy.conan-extension"
]
}
}
}

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
/build
# ---> C++
# Prerequisites
*.d

View File

@ -1,25 +0,0 @@
cmake_minimum_required(VERSION 3.0)
project(3rdparty-boost)
add_library(
${PROJECT_NAME}
STATIC
empty.cpp
)
set_property(
TARGET ${PROJECT_NAME}
PROPERTY CXX_STANDARD 20
)
target_link_libraries(
${PROJECT_NAME}
INTERFACE
/home/bart/Development/3rd-party/boost/boost_1_81_0/lib/libboost_url.a
/home/bart/Development/3rd-party/boost/boost_1_81_0/lib/libboost_json.a
)
target_include_directories(
${PROJECT_NAME}
PUBLIC /home/bart/Development/3rd-party/boost/boost_1_81_0/include/
)

View File

@ -1,37 +0,0 @@
cmake_minimum_required(VERSION 3.0)
project(3rdparty-googletest)
find_package(GTest REQUIRED)
add_library(
${PROJECT_NAME}
STATIC
empty.cpp
)
target_link_libraries(
${PROJECT_NAME}
INTERFACE
GTest::gtest
GTest::gmock
)
project(3rdparty-googletest-main)
add_library(
${PROJECT_NAME}
STATIC
empty.cpp
)
target_link_libraries(
${PROJECT_NAME}
INTERFACE
3rdparty-googletest
GTest::gtest_main
)

View File

@ -1,8 +1,7 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.20)
project(all)
enable_testing()
include(3rdparty-boost.cmake)
include(3rdparty-googletest.cmake)
add_subdirectory(bmrshared)
add_subdirectory(bmrshared-magic)

View File

@ -0,0 +1,2 @@
add_subdirectory(lib)
add_subdirectory(tst)

View File

@ -0,0 +1,26 @@
// GNU Lesser General Public License v3.0
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License v3.0 as published by
// the Free Software Foundation.
//
#include <filesystem>
#include <magic.h>
namespace bmrshared
{
class magic_file_info final
{
public:
explicit magic_file_info(const std::filesystem::path& path);
~magic_file_info();
std::string get_mime(const std::filesystem::path& path) const;
private:
magic_t m_magic_cookie;
};
} // namespace bmrshared

View File

@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.20)
find_package(libmagic 5.45 REQUIRED)
project(bmrshared-magic)
add_library(
${PROJECT_NAME}
magic_file_info.cpp
)
set_property(
TARGET ${PROJECT_NAME}
PROPERTY CXX_STANDARD 20
)
target_include_directories(
${PROJECT_NAME}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include
)
target_link_libraries(
${PROJECT_NAME}
PUBLIC
libmagic::libmagic
)
install(
FILES ${CONAN_LIBMAGIC_PACKAGE_FOLDER}/res/magic.mgc
DESTINATION bin
)

View File

@ -0,0 +1,59 @@
// GNU Lesser General Public License v3.0
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License v3.0 as published by
// the Free Software Foundation.
//
#include <bmrshared/magic_file_info.hpp>
namespace bmrshared
{
magic_file_info::magic_file_info(const std::filesystem::path& path)
: m_magic_cookie(nullptr)
{
bool load_ok = false;
m_magic_cookie = magic_open(MAGIC_MIME_TYPE);
if (m_magic_cookie != nullptr && std::filesystem::is_regular_file(path))
{
load_ok = (magic_load(m_magic_cookie, path.native().c_str()) == 0);
}
if (m_magic_cookie && !load_ok)
{
magic_close(m_magic_cookie);
m_magic_cookie = nullptr;
}
if (!load_ok)
{
throw std::runtime_error("Could not initialize libmagic.");
}
}
magic_file_info::~magic_file_info()
{
magic_close(m_magic_cookie);
}
std::string magic_file_info::get_mime(const std::filesystem::path& path) const
{
std::string mime;
if (std::filesystem::is_regular_file(path))
{
const char* file_name = path.native().c_str();
const char* magic_full = nullptr;
if (file_name != nullptr)
{
magic_full = magic_file(m_magic_cookie, file_name);
}
if (magic_full != nullptr)
{
mime = magic_full;
}
}
return mime;
}
} // namespace bmrshared

View File

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.20)
find_package(GTest REQUIRED)
include(GoogleTest)
project(bmrshared-magic-test)
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
add_executable(
${PROJECT_NAME}
test_magic.cpp
)
set_property(
TARGET ${PROJECT_NAME}
PROPERTY CXX_STANDARD 20
)
target_link_libraries(
${PROJECT_NAME}
PUBLIC
bmrshared-magic
GTest::gtest_main
)

View File

@ -0,0 +1,26 @@
// GNU Lesser General Public License v3.0
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License v3.0 as published by
// the Free Software Foundation.
//
#include <bmrshared/magic_file_info.hpp>
#include <gtest/gtest.h>
namespace
{
constexpr std::string_view magic_file_location = "/usr/local/bin/magic.mgc";
}
TEST(test_magic, no_throw_on_query)
{
bmrshared::magic_file_info mfi(magic_file_location);
EXPECT_NO_THROW(mfi.get_mime(magic_file_location));
}
TEST(test_magic, throw_on_wrong_magic_file)
{
EXPECT_THROW(bmrshared::magic_file_info mfi(""), std::exception);
EXPECT_THROW(bmrshared::magic_file_info mfi("/etc/hostname"), std::exception);
}

View File

@ -28,6 +28,7 @@ class flexible_value final
std::vector<int64_t>,
std::vector<uint64_t>,
std::vector<std::string>,
std::vector<flexible_value>,
keyvalue_type>;
flexible_value();

View File

@ -23,7 +23,8 @@ struct function_wrapper;
template<typename T, typename... TExtraArguments>
requires(std::is_const<T>::value == true) struct function_wrapper<T, TExtraArguments...>
requires(std::is_const<T>::value == true)
struct function_wrapper<T, TExtraArguments...>
{
using wrapper_type = std::function<flexible_value(const T&, const IDataSource&, TExtraArguments...)>;
@ -33,14 +34,15 @@ requires(std::is_const<T>::value == true) struct function_wrapper<T, TExtraArgum
std::array<std::string, sizeof...(TSourceParams)> names{paramNames...};
return [callableFn, names](const T& obj, const IDataSource& source, TExtraArguments... args)
{
return invoke(obj, callableFn, names, source, args...);
return invoke<M, T, sizeof...(TSourceParams), TExtraArguments...>(obj, callableFn, names, source, args...);
};
}
};
template<typename T, typename... TExtraArguments>
requires(std::is_const<T>::value == false) struct function_wrapper<T, TExtraArguments...>
requires(std::is_const<T>::value == false)
struct function_wrapper<T, TExtraArguments...>
{
using wrapper_type = std::function<flexible_value(T&, const IDataSource&, TExtraArguments...)>;
@ -50,7 +52,7 @@ requires(std::is_const<T>::value == false) struct function_wrapper<T, TExtraArgu
std::array<std::string, sizeof...(TSourceParams)> names{paramNames...};
return [callableFn, names](T& obj, const IDataSource& source, TExtraArguments... args)
{
return invoke(obj, callableFn, names, source, args...);
return invoke<M, T, sizeof...(TSourceParams), TExtraArguments...>(obj, callableFn, names, source, args...);
};
}
};

View File

@ -58,7 +58,7 @@ requires(function_info<M T::*>::is_const_member == false)
M T::*callableFn,
std::array<std::string, TParameterCount> names,
const IDataSource& source,
TExtraArguments... arguments)
TExtraArguments&&... arguments)
{
using CallableInfo = function_info<decltype(callableFn)>;
using SourceArgumentsTuple =
@ -78,7 +78,7 @@ requires(function_info<M T::*>::is_const_member == true)
M T::*callableFn,
std::array<std::string, TParameterCount> names,
const IDataSource& source,
TExtraArguments... arguments)
TExtraArguments&&... arguments)
{
using CallableInfo = function_info<decltype(callableFn)>;
using SourceArgumentsTuple =
@ -95,7 +95,7 @@ template<typename TCallable, std::size_t TParameterCount, typename... TExtraArgu
flexible_value invoke(TCallable& fn,
std::array<std::string, TParameterCount> names,
const IDataSource& source,
TExtraArguments... arguments)
TExtraArguments&&... arguments)
{
return invoke(fn, &TCallable::operator(), names, source, arguments...);
}

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.20)
project(bmrshared)

View File

@ -6,6 +6,7 @@
// the Free Software Foundation.
//
#include "bmrshared/to_tuple.hpp"
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>

View File

@ -1,4 +1,7 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.20)
find_package(GTest REQUIRED)
include(GoogleTest)
project(bmrshared-test)
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
@ -19,10 +22,7 @@ target_link_libraries(
${PROJECT_NAME}
PUBLIC
bmrshared
3rdparty-googletest-main
)
GTest::gtest_main
GTest::gmock
install(
TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
)

View File

@ -123,3 +123,22 @@ TEST(InvokeTest, Ok_Ptr_Member_Const)
flexible_value returnValue = bmrshared::invoke(functor, &TestAddition::AddNumbersConst, names, mockSrc);
EXPECT_EQ(returnValue.as<uint64_t>(), 140);
}
TEST(InvokeTest, Ok_ExtraArgumentByRefChanged)
{
auto fn = [](int64_t a, int64_t& lastArg)
{
lastArg = a;
return true;
};
std::array<std::string, 1> names = {"a"};
MockDataSource mockSrc;
EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector<std::string>{"a"}));
EXPECT_CALL(mockSrc, Get("a", An<int64_t&>())).WillRepeatedly(SetArgReferee<1>(42));
int64_t lastArg = 0;
flexible_value returnValue = bmrshared::invoke(fn, names, mockSrc, lastArg);
EXPECT_EQ(returnValue.as<bool>(), true);
EXPECT_EQ(lastArg, 42);
}

View File

@ -31,6 +31,17 @@ class TestAddition
int64_t AddNumbersConst(int64_t left, int64_t right) const { return left + (right * 2); }
};
class TestWriteOutputArgument
{
public:
bool WriteOutArg(int64_t a, int64_t& extraOutArg) const
{
return extraOutArg = a;
return true;
}
};
using TestWrapperType = function_wrapper<TestAddition>;
using TestConstWrapperType = function_wrapper<const TestAddition>;
@ -77,3 +88,20 @@ TEST(WrapTest, Const_Ptr_to_member_as_non_const)
flexible_value returnValue = wrapped(obj, mockSrc);
EXPECT_EQ(returnValue.as<int64_t>(), 84);
}
TEST(WrapTest, WriteExtraOutArg)
{
const TestWriteOutputArgument obj;
using TestWrapperOutArgType = function_wrapper<const TestWriteOutputArgument, int64_t&>;
TestWrapperOutArgType::wrapper_type wrapped =
TestWrapperOutArgType::Wrap(&TestWriteOutputArgument::WriteOutArg, "a");
MockDataSource mockSrc;
EXPECT_CALL(mockSrc, GetKeys()).WillRepeatedly(Return(std::vector<std::string>{"a"}));
EXPECT_CALL(mockSrc, Get("a", An<int64_t&>())).WillRepeatedly(SetArgReferee<1>(42));
int64_t lastArg = 0;
flexible_value returnValue = wrapped(obj, mockSrc, lastArg);
EXPECT_EQ(returnValue.as<bool>(), true);
EXPECT_EQ(lastArg, 42);
}

20
conanfile.py Normal file
View File

@ -0,0 +1,20 @@
import os
from conan import ConanFile
from conan.tools.files import copy
from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps
class HelloConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
requires = "boost/1.84.0", "gtest/1.14.0", "libmagic/5.45"
generators = "CMakeDeps"
build_policy = "*"
def generate(self):
# We need to find the folder of libmagic and supply it to cmake so that
# we can deploy the magic file.
libmagic = self.dependencies["libmagic"]
tc = CMakeToolchain(self)
tc.variables["CONAN_LIBMAGIC_PACKAGE_FOLDER"] = libmagic.package_folder
tc.generate()