From 43931c7e187bda85e2be12bccb14bd826a6c34c0 Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Sat, 20 Apr 2019 01:54:54 +0200 Subject: Add custom memory management strategies to cppc4group --- CMakeLists.txt | 1 + examples/cppc4group_memorymanagement.cpp | 51 ++++++++++++++++++++ src/cc4group.h | 4 +- src/cppc4group.cpp | 81 ++++++++++++++++++++++++++------ src/cppc4group.hpp | 41 ++++++++++++---- 5 files changed, 152 insertions(+), 26 deletions(-) create mode 100644 examples/cppc4group_memorymanagement.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4604c81..c3a5d02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ example(c4rm) example(c4mkdir) examplepp(c4ls_asChildPp) +examplepp(cppc4group_memorymanagement) add_executable(c4cat examples/c4cat.c) target_link_libraries(c4cat PRIVATE cc4group) diff --git a/examples/cppc4group_memorymanagement.cpp b/examples/cppc4group_memorymanagement.cpp new file mode 100644 index 0000000..b29adae --- /dev/null +++ b/examples/cppc4group_memorymanagement.cpp @@ -0,0 +1,51 @@ +#include "cppc4group.hpp" +#include +#include +#include + +using namespace std::literals; + +int main() +{ + CppC4Group g; + g.create(); + + constexpr auto takeContents = "This is only some dummy text for testing."sv; + void* takeData = malloc(takeContents.size()); + memcpy(takeData, takeContents.data(), takeContents.size()); + + auto takeContentsString = new std::string{takeContents}; + + (g.create() && + g.createFile("Take") && + g.createFile("Reference") && + g.createFile("Copy") && + g.createFile("Custom") && + g.createFile("CustomNoOpStart") && + + + g.setEntryData("Take", takeData, takeContents.size(), CppC4Group::MemoryManagement::Take) && + g.setEntryData("Reference", takeContents.data(), takeContents.size(), CppC4Group::MemoryManagement::Reference) && + g.setEntryData("Copy", takeContents.data(), takeContents.size(), CppC4Group::MemoryManagement::Copy) && + g.setEntryData("Custom", takeContents.data(), takeContents.size(), { + [](void* const data, size_t const size) -> void* + { + auto retData = new char[size]; + memcpy(retData, data, size); + return retData; + }, + [](void* const data) + { + delete [] reinterpret_cast(data); + } + }) && + g.setEntryData("CustomNoOpStart", takeContentsString->data(), takeContentsString->size(), {CppC4Group::MemoryManagement::NoOpStart, + [takeContentsString](void* const) + { + delete takeContentsString; + } + }) && + + g.save("cppc4group_memorymanagement.c4d")) || (std::cerr << g.getErrorMessage() << '\n', true); + return 0; +} \ No newline at end of file diff --git a/src/cc4group.h b/src/cc4group.h index 780c50e..2309338 100644 --- a/src/cc4group.h +++ b/src/cc4group.h @@ -44,7 +44,7 @@ // ...it will be a great help in debugging mistakes that are made by the programmer // for lazy writers or just some testing, the nature of the API actually makes it possible to just chain a bunch of API calls with && and... // ...thanks to short-circuiting it will stop exactly at the error and the whole expression will return false and the error information can be retrieved as usual -// sadly, this trick didn't seem to make it into any example, as they are written to output additional, more user-friendly, information together with the error message +// this can be seen in the cppc4group_memorymanagement example (and works the same for cc4group) // - finally (and i still forgot some other general aspect used for sure), because the return value of the functions is already used for the success-bool, ... // ...functions that should actually return some real data have one (or more) pointer-arguments where they store the returned information // - if you are such a patient reader and you made it here, you may now look at some of the examples (in the examples folder) @@ -146,7 +146,7 @@ typedef struct { void* (*start)(void* const data, const size_t size, void* const arg); void (*end)(void* const data, void* const arg); void* arg; -} const CC4Group_MemoryManagement_t; +} CC4Group_MemoryManagement_t; typedef CC4Group_MemoryManagement_t* CC4Group_MemoryManagement; diff --git a/src/cppc4group.cpp b/src/cppc4group.cpp index 2d85537..329d6de 100644 --- a/src/cppc4group.cpp +++ b/src/cppc4group.cpp @@ -39,6 +39,66 @@ struct CppC4Group::Private { } }; +struct CppC4Group::MemoryManagement::Private { + bool predefined; + MemoryManagementStrategy strategy; + + Start start; + End end; +}; + +CppC4Group::MemoryManagement::MemoryManagement(MemoryManagementStrategy strategy) : p{new Private{true, strategy}} +{ + +} + +CppC4Group::MemoryManagement::MemoryManagement(Start&& start, End&& end) : p{new Private{false, {}, start, end}} +{ + +} + +CppC4Group::MemoryManagement::~MemoryManagement() +{ + delete p; +} + +CppC4Group::MemoryManagement::MemoryManagementStrategy CppC4Group::MemoryManagement::operator()() const +{ + if(p->predefined) + { + return p->strategy; + } + else + { + struct CustomMemoryManagement { + CC4Group_MemoryManagement_t realMemoryManagement; + + Start start; + End end; + + static void* callStart(void* const data, size_t const size, void* const arg) + { + return reinterpret_cast(arg)->start(data, size); + } + + static void callEndAndCleanup(void* const data, void* const arg) + { + auto customMemoryManagement = reinterpret_cast(arg); + customMemoryManagement->end(data); + delete customMemoryManagement; + } + }; + + auto customManagement = new CustomMemoryManagement{{CustomMemoryManagement::callStart, CustomMemoryManagement::callEndAndCleanup}, p->start, p->end}; + customManagement->realMemoryManagement.arg = static_cast(customManagement); + return reinterpret_cast(&customManagement->realMemoryManagement); + } +} + +const CppC4Group::MemoryManagement CppC4Group::MemoryManagement::Take{reinterpret_cast(cc4group.MemoryManagement.Take)}; +const CppC4Group::MemoryManagement CppC4Group::MemoryManagement::Copy{reinterpret_cast(cc4group.MemoryManagement.Copy)}; +const CppC4Group::MemoryManagement CppC4Group::MemoryManagement::Reference{reinterpret_cast(cc4group.MemoryManagement.Reference)}; + namespace { CC4Group_TmpMemoryStrategy convertTmpMemoryStrategy(const CppC4Group::TmpMemoryStrategy strategy) { @@ -54,22 +114,13 @@ namespace { return cc4group.TmpMemoryStrategies.Auto; } - CC4Group_MemoryManagement convertMemoryManagement(const CppC4Group::MemoryManagement management) + CC4Group_MemoryManagement convertMemoryManagement(const CppC4Group::MemoryManagement& memoryManagement) { - switch(management) - { - case CppC4Group::Take: - return cc4group.MemoryManagement.Take; - case CppC4Group::Copy: - return cc4group.MemoryManagement.Copy; - case CppC4Group::Reference: - return cc4group.MemoryManagement.Reference; - } - return cc4group.MemoryManagement.Copy; + return reinterpret_cast(memoryManagement()); } } -CppC4Group::CppC4Group() : p{new CppC4Group::Private{}} +CppC4Group::CppC4Group() : p{new Private{}} { } @@ -110,12 +161,12 @@ bool CppC4Group::openFilePointer(FILE* file) return cc4group.openFilePointer(p->g, file); } -bool CppC4Group::openMemory(const void* const data, const size_t size, const MemoryManagement management) +bool CppC4Group::openMemory(const void* const data, const size_t size, const MemoryManagement& management) { return cc4group.openMemory(p->g, data, size, convertMemoryManagement(management)); } -bool CppC4Group::openWithReadCallback(const ReadCallback callback, void* const callbackArg, const MemoryManagement management, SetupCallback initCallback, SetupCallback deinitCallback) +bool CppC4Group::openWithReadCallback(const ReadCallback callback, void* const callbackArg, const MemoryManagement& management, SetupCallback initCallback, SetupCallback deinitCallback) { return cc4group.openWithReadCallback(p->g, callback, callbackArg, convertMemoryManagement(management), initCallback, deinitCallback); } @@ -311,7 +362,7 @@ bool CppC4Group::createFile(const std::string& path) return cc4group.createFile(p->g, path.c_str()); } -bool CppC4Group::setEntryData(const std::string& path, const void*const data, const size_t size, const MemoryManagement management) +bool CppC4Group::setEntryData(const std::string& path, const void*const data, const size_t size, const MemoryManagement& management) { return cc4group.setEntryData(p->g, path.c_str(), data, size, convertMemoryManagement(management)); } diff --git a/src/cppc4group.hpp b/src/cppc4group.hpp index f11387c..d946cee 100644 --- a/src/cppc4group.hpp +++ b/src/cppc4group.hpp @@ -76,6 +76,35 @@ public: // or an empty optional on failure using TmpMemoryCallback = std::optional(*)(size_t size); + // the C++ memory management strategies work exactly the same way as in C, but with std::function instead + class MemoryManagement { + public: + struct MemoryManagementStrategy_t; + using MemoryManagementStrategy = MemoryManagementStrategy_t*; + + using Start = std::function; + using End = std::function; + + private: + struct Private; + Private* p; + + public: + MemoryManagement(Start&& start, End&& end); + MemoryManagement(MemoryManagementStrategy strategy); + + ~MemoryManagement(); + + MemoryManagementStrategy operator()() const; + + // this may be used as start function if it need not do anything but return the original pointer + static constexpr auto NoOpStart = [](void* memory, size_t) { return memory; }; + + static const MemoryManagement Take; + static const MemoryManagement Copy; + static const MemoryManagement Reference; + }; + // these are equivalent to their C counterparts using ReadCallback = bool(*)(const void** const data, size_t* const size, void* const arg); using SetupCallback = bool(*)(void* const arg); @@ -88,12 +117,6 @@ public: Auto }; - enum MemoryManagement { - Take, - Copy, - Reference - }; - // use this to set one of the predefined strategies static void setTmpMemoryStrategy(const TmpMemoryStrategy strategy); @@ -115,8 +138,8 @@ public: bool openExisting(const std::string& path); bool openFd(const int fd); bool openFilePointer(FILE* file); - bool openMemory(const void* const data, const size_t size, const MemoryManagement management = Reference); - bool openWithReadCallback(const ReadCallback callback, void* const callbackArg, const MemoryManagement management = Take, SetupCallback initCallback = nullptr, SetupCallback deinitCallback = nullptr); + bool openMemory(const void* const data, const size_t size, const MemoryManagement& management = MemoryManagement::Reference); + bool openWithReadCallback(const ReadCallback callback, void* const callbackArg, const MemoryManagement& management = MemoryManagement::Take, SetupCallback initCallback = nullptr, SetupCallback deinitCallback = nullptr); // save actually maps to both cc4group.save and cc4group.saveOverwrite, thanks to default arguments (yes, thats the reason why they are separate in the C-API) bool save(const std::string& path, const bool overwrite = false); @@ -148,7 +171,7 @@ public: bool createFile(const std::string& path); - bool setEntryData(const std::string& path, const void* const data = nullptr, const size_t size = 0, const MemoryManagement management = Copy); + bool setEntryData(const std::string& path, const void* const data = nullptr, const size_t size = 0, const MemoryManagement& management = MemoryManagement::Copy); // to get the child group out of the optional in case of success, construct a new CppC4Group with the move constructor: CppC4Group child{std::move(*optionalChild)}; std::optional openAsChild(const std::string& path); -- cgit v1.2.3-54-g00ecf