Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rfl::DefaultIfMissing processor + Reflector<FixedVector> #260

Open
AnsiV01 opened this issue Nov 15, 2024 · 4 comments
Open

rfl::DefaultIfMissing processor + Reflector<FixedVector> #260

AnsiV01 opened this issue Nov 15, 2024 · 4 comments

Comments

@AnsiV01
Copy link
Contributor

AnsiV01 commented Nov 15, 2024

Hello

I am working on a mini-game where I am using reflect-cpp as a parser for json files and more. At some point I needed to implement support for a custom FixedVector class from the fixed_container library (https://github.com/teslamotors/fixed-containers). And here I ran into a problem, because the compiler cannot handle this class if I add a DefaultIfMissing processor when using rfl::json::(read/load). Below MRE.

#include <rfl/json.hpp>
#include <rfl/parsing/CustomParser.hpp>
#include <fixed_containers/fixed_string.hpp>

namespace rfl
{
	template <typename T, size_t Size>
	struct Reflector<fixed_containers::FixedVector<T, Size>>
	{
		using ReflType = std::vector<T>;
		static constexpr fixed_containers::FixedVector<T, Size> to(const ReflType& v)
		{
			if (v.size() > Size)
				throw std::exception("v.size() > Size");

			fixed_containers::FixedVector<T, Size> cont{};
			cont.assign(v.begin(), v.end());
			return cont;
		}

		static constexpr ReflType from(const fixed_containers::FixedVector<T, Size>& v)
		{
			ReflType cont{};
			cont.assign(v.begin(), v.end());
			return cont;
		}
	};
}

enum class Attributes
{
	NONE,
	MOVE_SPEED,
	ATTACK_SPEED,
	MAX_HP,
	MAX_MP,
};

struct Data
{
	fixed_containers::FixedVector<std::pair<Attributes, long long>, 3> attributes{};
};

int main()
{
	auto json1 = rfl::json::read<Data>(""); //this is OK
	auto json2 = rfl::json::load<Data>("data.json"); // this is OK

	auto json3 = rfl::json::read<Data, rfl::DefaultIfMissing>(""); //this fail
	auto json4 = rfl::json::load<Data, rfl::DefaultIfMissing>("data.json"); // this fail
}

The compiler (MSVC) reports a problem with the use of the deleted function:

Błąd C2280 „fixed_containers::FixedVector<std::pair<Attributes,__int64>,3,fixed_containers::customize::SequenceContainerAbortChecking<T,3>> &fixed_containers::FixedVector<T,3,fixed_containers::customize::SequenceContainerAbortChecking<T,3>>::operator =(const fixed_containers::FixedVector<T,3,fixed_containers::customize::SequenceContainerAbortChecking<T,3>> &)”: próba odwania do usuniętej funkcji with [ T=std::pair<Attributes,__int64> ] 121 E:\ecpp\ecpp\_third\rfl\parsing\ViewReaderWithDefault.hpp

And when I click on the error it redirects me to this code from the rfl\parsing\ViewReaderWithDefault.hpp:

template <class Target, class Source>
static void move_to(Target* _t, Source* _s) {
  if constexpr (std::is_const_v<Target>) {
    return move_to(const_cast<std::remove_const_t<Target>*>(_t), _s);
  } else if constexpr (!rfl::internal::is_array_v<Source> &&
                       !std::is_array_v<Target>) {
    *_t = Target(std::move(*_s));            <<-- ERROR HAPPENED HERE
  } else if constexpr (rfl::internal::is_array_v<Source>) {
    static_assert(std::is_array_v<Target>,
                  "Expected target to be a c-array.");
    for (size_t i = 0; i < _s->arr_.size(); ++i) {
      move_to(&((*_t)[i]), &(_s->arr_[i]));
    }
  } else {
    for (size_t i = 0; i < _s->size(); ++i) {
      move_to(&((*_t)[i]), &((*_s)[i]));
    }
  }
}

My question is whether there is a workaround or fix for this situation, or whether this is a problem that cannot be circumvented by the way the fixed_container library is implemented?

Thanks for any information.

@liuzicheng1987
Copy link
Contributor

Hi @AnsiV01 , it appears that the move operation is failing. Could you translate the error message to English?

@AnsiV01
Copy link
Contributor Author

AnsiV01 commented Nov 15, 2024

@liuzicheng1987 Sure:

Severity	Code	Description	Line	File	Project	Suppression State	Details
Error	C2280	'fixed_containers::FixedVector<std::pair<Attributes,__int64>,3,fixed_containers::customize::SequenceContainerAbortChecking<T,3>> &fixed_containers::FixedVector<T,3,fixed_containers::customize::SequenceContainerAbortChecking<T,3>>::operator =(const fixed_containers::FixedVector<T,3,fixed_containers::customize::SequenceContainerAbortChecking<T,3>> &)': attempting to reference a deleted function
        with
        [
            T=std::pair<Attributes,__int64>
        ]	121	E:\ecpp\ecpp\_third\rfl\parsing\ViewReaderWithDefault.hpp	Common		

@AnsiV01
Copy link
Contributor Author

AnsiV01 commented Nov 19, 2024

Today I had some free time and decided to poke around in the code of this library and found the reason for the failure. It's because move and copy assignment operator are set as default, and there are other contructors besides them, which makes them treated as delete. And so, for example, FixedVector can be initialised via std::initialised_list or by iteration.

I think support for this type of situation could be considered. For example, in my situation, a workaround is to change the aforementioned move_to function so that it uses requires to check if it can use the Move assignment operator, otherwise it will use the assign function of the FixedVector class.

template <class Target, class Source>
static void move_to(Target* _t, Source* _s) {
  if constexpr (std::is_const_v<Target>) {
    return move_to(const_cast<std::remove_const_t<Target>*>(_t), _s);
  } else if constexpr (!rfl::internal::is_array_v<Source> &&
                       !std::is_array_v<Target>) {
    //*_t = Target(std::move(*_s));
    //Workaround
    if constexpr (requires { *_t = Target(std::move(*_s)); })
      *_t = Target(std::move(*_s));
    else if constexpr (requires { _t->assign(_s->begin(), _s->end()); })
       _t->assign(_s->begin(), _s->end());
    else
      static_assert(rfl::always_false_v<Target>, "Unsupported");
    //Workaround
  } else if constexpr (rfl::internal::is_array_v<Source>) {
    static_assert(std::is_array_v<Target>,
                  "Expected target to be a c-array.");
    for (size_t i = 0; i < _s->arr_.size(); ++i) {
      move_to(&((*_t)[i]), &(_s->arr_[i]));
    }
  } else {
    for (size_t i = 0; i < _s->size(); ++i) {
      move_to(&((*_t)[i]), &((*_s)[i]));
    }
  }
}

@liuzicheng1987
Copy link
Contributor

liuzicheng1987 commented Nov 20, 2024

@AnsiV01 , I see...but this assumes that the underlying container has an assign-method.

To be honest, I would assume that the move assignment operator would be implemented for all non-const containers. I don't really see a good reason why the FixedVector library would not support the move assignment operator, but instead support something as non-standard as this assign method.

In my view, if the authors of the FixedVector library inadvertently deleted their move assignment operator, this is not something we should fix on our side. Maybe you should file a PR to their library and fix the issue there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants