add visitor pattern implementation for Poco::Dynamic::Var (#4144)

* add visitor pattern implementation for Poco::Dynamic::Var

* add changes to Makefile and vcxproj for VarVisitor

* resolve review comments Poco::Dynamic::Var

---------

Co-authored-by: Alexander B <bas524@ya.ru>
This commit is contained in:
Alexander B 2023-11-11 21:18:12 +03:00 committed by GitHub
parent a9f889f5cf
commit 6a5387ec21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 314 additions and 6 deletions

View File

@ -1463,6 +1463,7 @@
<ClCompile Include="src\Var.cpp" />
<ClCompile Include="src\VarHolder.cpp" />
<ClCompile Include="src\VarIterator.cpp" />
<ClCompile Include="src\VarVisitor.cpp" />
<ClCompile Include="src\Void.cpp" />
<ClCompile Include="src\Windows1250Encoding.cpp" />
<ClCompile Include="src\Windows1251Encoding.cpp" />
@ -1843,4 +1844,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -1463,6 +1463,7 @@
<ClCompile Include="src\Var.cpp" />
<ClCompile Include="src\VarHolder.cpp" />
<ClCompile Include="src\VarIterator.cpp" />
<ClCompile Include="src\VarVisitor.cpp" />
<ClCompile Include="src\Void.cpp" />
<ClCompile Include="src\Windows1250Encoding.cpp" />
<ClCompile Include="src\Windows1251Encoding.cpp" />
@ -1843,4 +1844,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -1469,6 +1469,7 @@
<ClCompile Include="src\Var.cpp" />
<ClCompile Include="src\VarHolder.cpp" />
<ClCompile Include="src\VarIterator.cpp" />
<ClCompile Include="src\VarVisitor.cpp" />
<ClCompile Include="src\Void.cpp" />
<ClCompile Include="src\Windows1250Encoding.cpp" />
<ClCompile Include="src\Windows1251Encoding.cpp" />
@ -1849,4 +1850,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -2052,6 +2052,7 @@
<ClCompile Include="src\Var.cpp" />
<ClCompile Include="src\VarHolder.cpp" />
<ClCompile Include="src\VarIterator.cpp" />
<ClCompile Include="src\VarVisitor.cpp" />
<ClCompile Include="src\Void.cpp" />
<ClCompile Include="src\Windows1250Encoding.cpp" />
<ClCompile Include="src\Windows1251Encoding.cpp" />
@ -2456,4 +2457,4 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@ -29,7 +29,7 @@ objects = ArchiveStrategy Ascii ASCIIEncoding AsyncChannel \
ThreadPool ThreadTarget ActiveDispatcher Timer Timespan Timestamp Timezone Token URI \
FileStreamFactory URIStreamFactory URIStreamOpener UTF32Encoding UTF16Encoding UTF8Encoding UTF8String \
Unicode UnicodeConverter Windows1250Encoding Windows1251Encoding Windows1252Encoding \
UUID UUIDGenerator Void Var VarHolder VarIterator Format Pipe PipeImpl PipeStream SharedMemory \
UUID UUIDGenerator Void Var VarHolder VarIterator VarVisitor Format Pipe PipeImpl PipeStream SharedMemory \
MemoryStream FileStream AtomicCounter DataURIStream DataURIStreamFactory
zlib_objects = adler32 compress crc32 deflate \

View File

@ -0,0 +1,99 @@
#ifndef Foundation_VarVisitor_INCLUDED
#define Foundation_VarVisitor_INCLUDED
//
// VarVisitor.h
//
// Library: Foundation
// Package: Dynamic
// Module: VarVisitor
//
// Definition of the VarVisitor class.
//
// Copyright (c) 2023, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Dynamic/Var.h"
#include <unordered_map>
#include <functional>
namespace Poco {
namespace Details {
struct TypeInfoHash
{
inline std::size_t operator()(std::type_info const& t) const { return t.hash_code(); }
};
struct EqualRef
{
template <typename T>
bool operator()(std::reference_wrapper<T> a, std::reference_wrapper<T> b) const
{
return a.get() == b.get();
}
};
using TypeInfoRef = std::reference_wrapper<std::type_info const>;
using HandlerCaller = std::function<void(const Poco::Dynamic::Var&)>;
template <typename T>
using HandlerPointer = void (*)(const T &);
template <typename T>
using Handler = std::function<void(const T&)>;
} // Details
namespace Dynamic {
class Foundation_API Visitor
/// VarVisitor class.
{
std::unordered_map<Details::TypeInfoRef,
Details::HandlerCaller,
Details::TypeInfoHash,
Details::EqualRef> _handlers;
public:
template <typename T>
bool addHandler(const Details::Handler<T> &f)
/// Add handler for specific type T which holds in Var
/// This method is more safe, because it saves copy of handler : lambda or std::function
/// Returns true if handler was added
{
auto result = _handlers.emplace(std::ref(typeid(T)),
Details::HandlerCaller([handler = f](const Poco::Dynamic::Var& x)
{
handler(x.extract<T>());
}));
return result.second;
}
template <typename T>
bool addHandler(Details::HandlerPointer<T> f)
/// Add handler for specific type T which holds in Var
/// This method is less safe, because it saves only copy of function pointer
/// Returns true if handler was added
{
auto result = _handlers.emplace(std::ref(typeid(T)),
Details::HandlerCaller([handlerPointer = f](const Poco::Dynamic::Var& x)
{
handlerPointer(x.extract<T>());
}));
return result.second;
}
bool visit(const Poco::Dynamic::Var& x) const;
/// Find handler for holded type and if it exists call handler
/// Returns true if hanler was found othrewise returns false
};
} } // namespace Poco::Dynamic
#endif // Foundation_VarVisitor_INCLUDED

View File

@ -0,0 +1,32 @@
//
// VarVisitor.cpp
//
// Library: Foundation
// Package: Dynamic
// Module: VarVisitor
//
//
// Copyright (c) 2023, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Dynamic/VarVisitor.h"
namespace Poco {
namespace Dynamic {
bool Visitor::visit(const Var &x) const
{
bool wasHandled = false;
auto it = _handlers.find(x.type());
if (it != _handlers.end())
{
it->second(x);
wasHandled = true;
}
return wasHandled;
}
} } // namespace Poco::Dynamic

View File

@ -16,8 +16,10 @@
#include "Poco/Bugcheck.h"
#include "Poco/Dynamic/Struct.h"
#include "Poco/Dynamic/Pair.h"
#include "Poco/Dynamic/VarVisitor.h"
#include <map>
#include <utility>
#include <iostream>
#if defined(_MSC_VER) && _MSC_VER < 1400
@ -48,10 +50,15 @@ public:
return _val;
}
bool operator == (int i)
bool operator == (int i) const
{
return i == _val;
}
friend bool operator == (const Dummy &d1, const Dummy &d2)
{
return d1._val == d2._val;
}
private:
int _val;
@ -3103,6 +3110,170 @@ void VarTest::testSharedPtr()
assertTrue(p.referenceCount() == 1);
}
struct ProcessDummy
{
Var &v;
ProcessDummy(Var &var) : v(var) {}
void operator()(const Dummy &d)
{
v = d;
}
};
#define ADD_HANDLER_FOR_TYPE_WITH_VALUE(Type, Handler, Value) \
visitor.addHandler<Type>(Handler); \
if (accepted) \
{ \
var.emplace_back(Type(Value));\
} \
else \
{ \
warn("handler already exists for " #Type "", __LINE__, __FILE__); \
} void(0)
void VarTest::testVarVisitor()
{
Visitor visitor;
Var processedVar;
auto processInt8 = [&processedVar](const Poco::Int8 &v) -> void
{
processedVar = v;
std::cout << " -> Poco::Int8 ";
};
auto processInt16 = [&processedVar](const Poco::Int16 &v) -> void
{
processedVar = v;
std::cout << " -> Poco::Int16 ";
};
auto processInt32 = [&processedVar](const Poco::Int32 &v) -> void
{
processedVar = v;
std::cout << " -> Poco::Int32 ";
};
auto processInt64 = [&processedVar](const Poco::Int64 &v) -> void
{
processedVar = v;
std::cout << " -> Poco::Int64 ";
};
auto processUInt8 = [&processedVar](const Poco::UInt8 &v) -> void
{
processedVar = v;
std::cout << " -> Poco::UInt8 ";
};
auto processUInt16 = [&processedVar](const Poco::UInt16 &v) -> void
{
processedVar = v;
std::cout << " -> Poco::UInt16 ";
};
auto processUInt32 = [&processedVar](const Poco::UInt32 &v) -> void
{
processedVar = v;
std::cout << " -> Poco::UInt32 ";
};
auto processUInt64 = [&processedVar](const Poco::UInt64 &v) -> void
{
processedVar = v;
std::cout << " -> Poco::UInt64 ";
};
auto processBool = [&processedVar](const bool &v) -> void
{
processedVar = v;
std::cout << " -> bool ";
};
auto processChar = [&processedVar](const char &v) -> void
{
processedVar = v;
std::cout << " -> char ";
};
auto processFloat = [&processedVar](const float &v) -> void
{
processedVar = v;
std::cout << " -> float ";
};
auto processDouble = [&processedVar](const double &v) -> void
{
processedVar = v;
std::cout << " -> double ";
};
auto processLong = [&processedVar](const long &v) -> void
{
processedVar = v;
std::cout << " -> long ";
};
auto processLongLong = [&processedVar](const long long &v) -> void
{
processedVar = v;
std::cout << " -> long long ";
};
auto processULong = [&processedVar](const unsigned long &v) -> void
{
processedVar = v;
std::cout << " -> unsigned long ";
};
auto processULongLong = [&processedVar](const unsigned long long &v) -> void
{
processedVar = v;
std::cout << " -> unsigned long long ";
};
auto processString = [&processedVar](const std::string &v) -> void
{
processedVar = v;
std::cout << " -> string ";
};
std::vector<Var> var;
using ulong = unsigned long;
using longlong = long long;
using ulonglong = unsigned long long;
ProcessDummy processDummy(processedVar);
bool accepted = false;
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Poco::Int8, processInt8, -8);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Poco::Int16, processInt16, -16);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Poco::Int32, processInt32, -32);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Poco::Int64, processInt64, -64);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Poco::UInt8, processUInt8, 8);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Poco::UInt16, processUInt16, 16);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Poco::UInt32, processUInt32, 32);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Poco::UInt64, processUInt64, 64);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(bool, processBool, true);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(char, processChar, 'f');
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(float, processFloat, 1.2f);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(double, processDouble, 2.4);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(long, processLong, 123L);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(ulong, processULong, 124UL);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(longlong, processLongLong, 123123LL);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(ulonglong, processULongLong, 124124ULL);
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(std::string, processString, "hello world");
accepted = ADD_HANDLER_FOR_TYPE_WITH_VALUE(Dummy, processDummy, 42);
for (const auto &v : var)
{
std::cout << "handle type : " << v.type().name();
if (visitor.visit(v))
{
if (v.type() != typeid(Dummy))
{
std::cout << " [" << v.toString() << "] ... ";
assertTrue(v == processedVar);
}
else
{
std::cout << " [" << v.extract<Dummy>() << "] ... ";
assertTrue(v.extract<Dummy>() == processedVar.extract<Dummy>());
}
std::cout << " ok" << '\n';
}
else
{
std::cout << " fail" << '\n';
fail(Poco::format("failed type handle : %s", v.type().name()), __LINE__, __FILE__);
}
}
}
void VarTest::setUp()
{
@ -3172,6 +3343,7 @@ CppUnit::Test* VarTest::suite()
CppUnit_addTest(pSuite, VarTest, testUUID);
CppUnit_addTest(pSuite, VarTest, testEmpty);
CppUnit_addTest(pSuite, VarTest, testIterator);
CppUnit_addTest(pSuite, VarTest, testVarVisitor);
return pSuite;
}

View File

@ -80,6 +80,7 @@ public:
void testEmpty();
void testIterator();
void testSharedPtr();
void testVarVisitor();
void setUp();
void tearDown();