use unique_ptr for memory management in constraints and subschema

This commit is contained in:
Marco Porsch 2021-12-08 12:19:49 +01:00
parent 4a99dd79d0
commit cf841e10e9
4 changed files with 47 additions and 39 deletions

View File

@ -36,14 +36,19 @@ struct BasicConstraint: Constraint
return visitor.visit(*static_cast<const ConstraintType*>(this));
}
Constraint * clone(CustomAlloc allocFn, CustomFree) const override
OwningPointer clone(CustomAlloc allocFn, CustomFree freeFn) const override
{
void *ptr = allocFn(sizeof(ConstraintType));
// smart pointer to automatically free raw memory on exception
auto ptr = std::unique_ptr<void, CustomFree>(allocFn(sizeof(ConstraintType)), freeFn);
if (!ptr) {
throwRuntimeError("Failed to allocate memory for cloned constraint");
}
return new (ptr) ConstraintType(*static_cast<const ConstraintType*>(this));
// constructor might throw but the memory will be taken care of anyways
(void)new (ptr.get()) ConstraintType(*static_cast<const ConstraintType*>(this));
// reassign managed memory to smart pointer that will also destroy object instance
return OwningPointer(static_cast<const Constraint*>(ptr.release()), freeFn);
}
protected:

View File

@ -903,26 +903,19 @@ public:
return visitor.visit(*static_cast<const PolyConstraint*>(this));
}
Constraint * clone(CustomAlloc allocFn, CustomFree freeFn) const override
OwningPointer clone(CustomAlloc allocFn, CustomFree freeFn) const override
{
void *ptr = allocFn(sizeOf());
// smart pointer to automatically free raw memory on exception
auto ptr = std::unique_ptr<void, CustomFree>(allocFn(sizeOf()), freeFn);
if (!ptr) {
throwRuntimeError("Failed to allocate memory for cloned constraint");
}
#if VALIJSON_USE_EXCEPTIONS
try {
#endif
return cloneInto(ptr);
#if VALIJSON_USE_EXCEPTIONS
} catch (...) {
freeFn(ptr);
throw;
}
#else
// pretend to use freeFn to avoid warning in GCC 8.3
(void)freeFn;
#endif
// constructor might throw but the memory will be taken care of anyways
(void)cloneInto(ptr.get());
// reassign managed memory to smart pointer that will also destroy object instance
return OwningPointer(static_cast<const Constraint*>(ptr.release()), freeFn);
}
virtual bool validate(const adapters::Adapter &target,

View File

@ -1,5 +1,8 @@
#pragma once
#include <memory>
#include <type_traits>
namespace valijson {
namespace constraints {
@ -18,6 +21,28 @@ struct Constraint
/// Typedef for custom free-like function
typedef void (*CustomFree)(void *);
/// Deleter type to be used with std::unique_ptr / std::shared_ptr
/// @tparam T Const or non-const type (same as the one used in unique_ptr/shared_ptr)
template<typename T>
struct CustomDeleter
{
CustomDeleter(CustomFree freeFn)
: m_freeFn(freeFn) { }
void operator()(T *ptr) const
{
auto *nonconst = const_cast<typename std::remove_const<T>::type *>(ptr);
nonconst->~T();
m_freeFn(nonconst);
}
private:
CustomFree m_freeFn;
};
/// Exclusive-ownership pointer to automatically handle deallocation
typedef std::unique_ptr<const Constraint, CustomDeleter<const Constraint>> OwningPointer;
/**
* @brief Virtual destructor.
*/
@ -42,7 +67,7 @@ struct Constraint
*
* @returns an owning-pointer to the new constraint.
*/
virtual Constraint * clone(CustomAlloc, CustomFree) const = 0;
virtual OwningPointer clone(CustomAlloc, CustomFree) const = 0;
};

View File

@ -79,11 +79,6 @@ public:
#if VALIJSON_USE_EXCEPTIONS
try {
#endif
for (auto constConstraint : m_constraints) {
auto *constraint = const_cast<Constraint *>(constConstraint);
constraint->~Constraint();
m_freeFn(constraint);
}
m_constraints.clear();
#if VALIJSON_USE_EXCEPTIONS
} catch (const std::exception &e) {
@ -106,18 +101,8 @@ public:
*/
void addConstraint(const Constraint &constraint)
{
Constraint *newConstraint = constraint.clone(m_allocFn, m_freeFn);
#if VALIJSON_USE_EXCEPTIONS
try {
#endif
m_constraints.push_back(newConstraint);
#if VALIJSON_USE_EXCEPTIONS
} catch (...) {
newConstraint->~Constraint();
m_freeFn(newConstraint);
throw;
}
#endif
// the vector allocation might throw but the constraint memory will be taken care of anyways
m_constraints.push_back(constraint.clone(m_allocFn, m_freeFn));
}
/**
@ -134,7 +119,7 @@ public:
bool apply(ApplyFunction &applyFunction) const
{
bool allTrue = true;
for (const Constraint *constraint : m_constraints) {
for (auto &&constraint : m_constraints) {
allTrue = applyFunction(*constraint) && allTrue;
}
@ -153,7 +138,7 @@ public:
*/
bool applyStrict(ApplyFunction &applyFunction) const
{
for (const Constraint *constraint : m_constraints) {
for (auto &&constraint : m_constraints) {
if (!applyFunction(*constraint)) {
return false;
}
@ -296,7 +281,7 @@ private:
bool m_alwaysInvalid;
/// List of pointers to constraints that apply to this schema.
std::vector<const Constraint *> m_constraints;
std::vector<Constraint::OwningPointer> m_constraints;
/// Schema description (optional)
opt::optional<std::string> m_description;