Skip to content

Commit

Permalink
Add support for described classes
Browse files Browse the repository at this point in the history
  • Loading branch information
pdimov committed Oct 26, 2024
1 parent b47b0ea commit 34c27e8
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 0 deletions.
54 changes: 54 additions & 0 deletions include/boost/hash2/hash_append.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#include <boost/container_hash/is_contiguous_range.hpp>
#include <boost/container_hash/is_unordered_range.hpp>
#include <boost/container_hash/is_tuple_like.hpp>
#include <boost/container_hash/is_described_class.hpp>
#include <boost/describe/bases.hpp>
#include <boost/describe/members.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <cstdint>
#include <type_traits>
Expand Down Expand Up @@ -262,6 +266,56 @@ template<class Hash, class Flavor, class T>
detail::hash_append_tuple( h, f, v, seq() );
}

// described classes

#if defined(BOOST_DESCRIBE_CXX14)

#if defined(_MSC_VER) && _MSC_VER == 1900
# pragma warning(push)
# pragma warning(disable: 4100) // unreferenced formal parameter
#endif

template<class Hash, class Flavor, class T>
typename std::enable_if< container_hash::is_described_class<T>::value, void >::type
do_hash_append( Hash& h, Flavor const& f, T const& v )
{
static_assert( !std::is_union<T>::value, "Described unions are not supported" );

std::size_t r = 0;

using Bd = describe::describe_bases<T, describe::mod_any_access>;

mp11::mp_for_each<Bd>([&](auto D){

using B = typename decltype(D)::type;
hash2::hash_append( h, f, (B const&)v );
++r;

});

using Md = describe::describe_members<T, describe::mod_any_access>;

mp11::mp_for_each<Md>([&](auto D){

hash2::hash_append( h, f, v.*D.pointer );
++r;

});

// A hash_append call must always result in a call to Hash::update

if( r == 0 )
{
hash2::hash_append( h, f, '\x00' );
}
}

#if defined(_MSC_VER) && _MSC_VER == 1900
# pragma warning(pop)
#endif

#endif // defined(BOOST_DESCRIBE_CXX14)

// hash_append

template<class Hash, class Flavor = default_flavor, class T> void hash_append( Hash& h, Flavor const& f, T const& v )
Expand Down
3 changes: 3 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ run append_string_view.cpp ;
run append_tuple_like.cpp ;
run append_set.cpp ;
run append_map.cpp ;
run append_described.cpp ;
run append_described_2.cpp ;
run append_described_3.cpp ;

run hash_append_5.cpp ;
run hash_append_range.cpp ;
Expand Down
49 changes: 49 additions & 0 deletions test/append_described.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2024 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/fnv1a.hpp>
#include <boost/describe/class.hpp>
#include <boost/core/lightweight_test.hpp>

struct X
{
int a;
int b;
};

BOOST_DESCRIBE_STRUCT(X, (), (a, b))

template<class Hash, class Flavor, class R> void test( X const& x, int const (&a)[ 2 ], R r )
{
Flavor f;

{
Hash h;
hash_append( h, f, x );

BOOST_TEST_EQ( h.result(), r );
}

{
Hash h;
hash_append( h, f, a );

BOOST_TEST_EQ( h.result(), r );
}
}

int main()
{
using namespace boost::hash2;

test<fnv1a_32, default_flavor>( {}, {}, 2615243109 );
test<fnv1a_32, little_endian_flavor>( {}, {}, 2615243109 );
test<fnv1a_32, big_endian_flavor>( {}, {}, 2615243109 );

test<fnv1a_32, little_endian_flavor>( { 1, 2 }, { 1, 2 }, 3738734694 );
test<fnv1a_32, big_endian_flavor>( { 1, 2 }, { 1, 2 }, 1396319868 );

return boost::report_errors();
}
61 changes: 61 additions & 0 deletions test/append_described_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2024 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/fnv1a.hpp>
#include <boost/describe/class.hpp>
#include <boost/core/lightweight_test.hpp>

struct X
{
int a = 1;
int b = 2;
};

BOOST_DESCRIBE_STRUCT(X, (), (a, b))

struct Y
{
int c = 3;
int d = 4;
};

BOOST_DESCRIBE_STRUCT(Y, (), (c, d))

struct Z: X, Y
{
int e = 5;
int f = 6;
};

BOOST_DESCRIBE_STRUCT(Z, (X, Y), (e, f))

template<class Hash, class Flavor, class R> void test( Z const& z, int const (&a)[ 6 ], R r )
{
Flavor f;

{
Hash h;
hash_append( h, f, z );

BOOST_TEST_EQ( h.result(), r );
}

{
Hash h;
hash_append( h, f, a );

BOOST_TEST_EQ( h.result(), r );
}
}

int main()
{
using namespace boost::hash2;

test<fnv1a_32, little_endian_flavor>( {}, { 1, 2, 3, 4, 5, 6 }, 2412827170 );
test<fnv1a_32, big_endian_flavor>( {}, { 1, 2, 3, 4, 5, 6 }, 1970495216 );

return boost::report_errors();
}
64 changes: 64 additions & 0 deletions test/append_described_3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2024 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/hash2/hash_append.hpp>
#include <boost/hash2/fnv1a.hpp>
#include <boost/describe/class.hpp>
#include <boost/core/lightweight_test.hpp>

struct X
{
};

BOOST_DESCRIBE_STRUCT(X, (), ())

template<class Hash, class Flavor> void test()
{
typename Hash::result_type r1, r2, r3;

Flavor f;

{
X x[ 1 ] = {};

Hash h;
hash_append( h, f, x );

r1 = h.result();
}

{
X x[ 2 ] = {};

Hash h;
hash_append( h, f, x );

r2 = h.result();
}

BOOST_TEST_NE( r2, r1 );

{
X x[ 3 ] = {};

Hash h;
hash_append( h, f, x );

r3 = h.result();
}

BOOST_TEST_NE( r3, r1 );
BOOST_TEST_NE( r3, r2 );
}

int main()
{
using namespace boost::hash2;

test<fnv1a_32, default_flavor>();
test<fnv1a_32, little_endian_flavor>();
test<fnv1a_32, big_endian_flavor>();

return boost::report_errors();
}

0 comments on commit 34c27e8

Please sign in to comment.