Coder Social home page Coder Social logo

Comments (8)

eao197 avatar eao197 commented on August 30, 2024

Hi!

It seems that the root of the problem is the use of platform-specific type std::size_t instead of std::uint64_t or something like that. But I think that you can't simply change std::size_t in data structures by something else.

So if we're speaking about handling that case at json_dto level then the simplest way could be the use of custom Reader_Writer for fields of type std::size_t. Something like that:

struct size_t_serializer
{
	void read( std::size_t & v, const rapidjson::Value & from ) const
	{
		using json_dto::read_json_value;

		std::uint64_t actual;
		read_json_value( actual, from );

		v = static_cast<std::size_t>(actual);
	}

	void
	write(
		const std::size_t & v,
		rapidjson::Value & to,
		rapidjson::MemoryPoolAllocator<> & allocator ) const
	{
		using json_dto::write_json_value;

		const std::uint64_t actual{v};
		write_json_value( actual, to, allocator );
	}
};
...
struct some_your_data_struct
{
	std::size_t payload_size_;
	std::string payload_;

	template< typename Json_Io >
	void json_io( Json_Io & io )
	{
		io & json_dto::mandatory( size_t_serializer{}, "size", payload_size_ )
			& json_dto::mandatory( "payload", payload_ );
	}
};

Another possible solution is new functionality added in v.0.2.12 (customization points in the form of binder_data_holder_t, binder_read_from_implementation_t and binder_write_to_implementation_t). It is similar to custom Reader_Writer, but requires more code.

from json_dto.

omartijn avatar omartijn commented on August 30, 2024

Thanks, @eao197

It's a shame that there's no "nice" way to do it, but it looks like that's due to the weird typedefs provided by the standard. To me, it'd make more sense if the standard declared that std::size_t should alias to std::uint116_t, std::uint32_t or std::uint64_t for 16, 32 and 64-bit architectures.

Would it be a solution to make the functions for numeric conversions templated, and then constrain them with SFINAE on being integral and having the correct size? Something like this:

template< typename type >
std::enable_if_t< std::is_integral_v< type > && std::is_unsigned_v< type > && sizeof( number_type ) == 4 >
read_json_value( type& value, const rapidjson::Value& object )
{
    if( object.IsUint32() )
        v = object.GetUint32();
    else
        throw ex_t{ "value is not Uint32" };
}

This would work for any unsigned type with 32-bits. If we define these templates for all relevant numeric types, we should catch them no matter whether they're aliased to unsigned long, unsigned long long, as long as they have the right size.

We can probably update the macros used for defining these functions to generate templated versions instead.

from json_dto.

eao197 avatar eao197 commented on August 30, 2024

Would it be a solution to make the functions for numeric conversions templated, and then constrain them with SFINAE on being integral and having the correct size?

I don't think it has the sense if a user is able to use fixed-size types like std::uint32_t.

From my experience in serializing/deserializing data in various formats, the only robust method is to use fixed-size types for data members. It means std::uint32_t instead of unsigned int on 32- and 64-bit platforms, std::uint64_t instead of unsigned long and so on. That approach solves a lot of problems accessing serialized data on different platforms and from different languages.

Complex template-based implementations of read_json_value (and similar functions) will add a lot more hidden magic to json_dto implementation and I think it's not a good thing.

My opinion is that the usage of fixed-size types like std::unit32_t and own strong typedefs for types like std::size_t (which can be different of different platforms) is much better and more obvious and straightforward.

from json_dto.

omartijn avatar omartijn commented on August 30, 2024

Well, the issue with std::size_t is that it is commonly used for for things like container sizes. Sometimes you want to only serialize something like that directly. It's convenient if that "just works".

This goes especially if you're using non-intrusive json_io. I find myself fighting with json_dto because on macOS it chokes on the size_t being used, so my simple, declarative mapping doesn't work and I have to explicitly start casting types.

from json_dto.

eao197 avatar eao197 commented on August 30, 2024

Well, the issue with std::size_t is that it is commonly used for for things like container sizes.

It's normal for in-process usage but can potentially lead to surprises when it's used in serialization/deserialization.

But I understand your case. Unfortunately, I don't see a good solution now. Let me return to the issue when I'll have some more time to think.

from json_dto.

eao197 avatar eao197 commented on August 30, 2024

@omartijn
Can you provide a small example of your code you have problems with? Such an example will allow me to understand better what you have and what you want to achieve.

from json_dto.

eao197 avatar eao197 commented on August 30, 2024

It seems that there are several ways to cope with that problem. All of them require to #include not json_dto/pub.hpp directly, but your helper header file. Let say that file has the name json_dto_wrapper.hpp. So you have to write:

#include <json_dto_wrapper.hpp> // or "json_dto_wrapper.hpp

instead of

#include <json_dto/pub.hpp>

In the simplest case json_dto_wrapper.hpp can looks like:

#pragma once

#include <json_dto/pub.hpp>

#if <check for Apple platform here>

namespace json_dto
{

// Introduce overloads for unsigned long.
RW_JSON_VALUES( unsigned long, IsUint64, GetUint64, SetUint64 )

}

#endif  

A more complex way is to introduce some SFINAE-protected function templates into json_dto namespace. Those templates have to be chosen by the compiler if it can't find appropriate non-templated functions. In that case json_dto_wrapper.hpp can looks like:

#pragma once

#include <json_dto/pub.hpp>

namespace json_dto
{

template< typename type >
std::enable_if_t< std::is_integral_v< type > && std::is_unsigned_v< type > && sizeof( type ) == sizeof( std::uint32_t ) >
read_json_value( type& value, const rapidjson::Value& object )
{
    if( object.IsUint32() )
        v = object.GetUint32();
    else
        throw ex_t{ "value is not Uint32" };
}
... // Similar version of write_json_value 
}

NOTE. I don't check this approach, but hope it should work.

And in that case, it is your duty to make sure that your type has the same length on 32- and 64-bit platforms.

The last way is to make your specialization of binder_read_from_implementation and binder_write_to_implementation customization points. An example of how it can be done can be found here. It's an overcomplicated proof-of-concept only and I don't advise using it in the production.

PS. We discussed that issue in our team and decided to not modify the source of json_dto. Becase overloads like shown here can lead to code that behaves differently on different platforms.

from json_dto.

omartijn avatar omartijn commented on August 30, 2024

I'm not sure how, but it seems I missed this response. I'm indeed using a check for libc++ now and adding the overload in that case, which isn't pretty, but I do understand your concerns.

from json_dto.

Related Issues (17)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.