jorgen / json_struct

json_struct is a single header only C++ library for parsing JSON directly to C++ structs and vice versa
Other
422 stars 57 forks source link

serial a class' private member #56

Closed yanzixiang closed 5 months ago

yanzixiang commented 6 months ago

i try to serial a class like this,

class a{
public:
  std::string b;
}

JS_OBJ_EXT(a,b);
a my_a;
auto json_string_a = JS::serializeStruct(my_a);

it works fine.

but when i add a private member c

class a{
public:
  std::string b;
private:
  std::string c;
}

JS_OBJ_EXT(a,b,c);

the complier says it can NOT access private member. so, how can i make it work again ?

jorgen commented 6 months ago

Yes, the serializer and parser needs access to the members. The parser and serializer will use member pointers declared by the JS_OBJ_EXT macro. There is however, a trick. If you use a "internal" JS_OBJ structure and make that public, then the serializer and parser can use that to access the members. Check out this godbolt that shows this: https://godbolt.org/z/7WznYTPn8

yanzixiang commented 6 months ago

wath if I can NOT edit the source code of class a ? and provide function c() and set_c() like this. https://github.com/USNavalResearchLaboratory/simdissdk/blob/main/SDK/simCore/Calc/Vec3.h

/* -*- mode: c++ -*- */
/****************************************************************************
 *****                                                                  *****
 *****                   Classification: UNCLASSIFIED                   *****
 *****                    Classified By:                                *****
 *****                    Declassify On:                                *****
 *****                                                                  *****
 ****************************************************************************
 *
 *
 * Developed by: Naval Research Laboratory, Tactical Electronic Warfare Div.
 *               EW Modeling & Simulation, Code 5773
 *               4555 Overlook Ave.
 *               Washington, D.C. 20375-5339
 *
 * License for source code is in accompanying LICENSE.txt file. If you did
 * not receive a LICENSE.txt with this code, email simdis@nrl.navy.mil.
 *
 * The U.S. Government retains all rights to use, duplicate, distribute,
 * disclose, or release this software.
 *
 */
#ifndef SIMCORE_CALC_VEC3_H
#define SIMCORE_CALC_VEC3_H

#include <cassert>
#include <cmath>
#include <cstdlib>

namespace simCore
{

// Header only class; doesn't need to be exported
/// (static sized) vector of three doubles
class Vec3
{
public:
  /// Default constructor gives {0,0,0}
  Vec3() { }

  /**
   * Value constructor
   * @param[in] v0 The component at index 0; pos/ori/vel/acc
   * @param[in] v1 The component at index 1; pos/ori/vel/acc
   * @param[in] v2 The component at index 2; pos/ori/vel/acc
   */
  Vec3(double v0, double v1, double v2) { set(v0, v1, v2); }

  /**
   * Value constructor
   * @param[in] v Vector elements
   * @pre valid input pointer, if not valid internal elements are set to zero
   */
  explicit Vec3(const double v[3])
  {
    if (v == nullptr)
    {
      zero();
      return;
    }
    set(v[0], v[1], v[2]);
  }

  /**@name Copy and move operators and assignment
   * @{
   */
  Vec3(const Vec3& rhs) = default;
  Vec3(Vec3&& rhs) = default;
  Vec3& operator=(const Vec3& rhs) = default;
  Vec3& operator=(Vec3&& rhs) = default;
  ///@}

  /// Equality
  bool operator==(const Vec3& value) const
  {
    if (&value == this) return true;
    return (v_[0] == value.v_[0] && v_[1] == value.v_[1] && v_[2] == value.v_[2]);
  }
  /// Inequality
  bool operator!=(const Vec3& value) const
  {
    return !operator==(value);
  }

  /// Set vector elements to zero
  void zero() { v_[0] = v_[1] = v_[2] = 0; }

  /**@name Read and Write Access
   * @{
   */
  /// Behave like an array
  double operator[](size_t index) const
  {
    assert(index < 3);
    if (index > 2)
      index = 2;
    return v_[index];
  }
  /// Behave like an array
  double& operator[](size_t index)
  {
    assert(index < 3);
    if (index > 2)
      index = 2;
    return v_[index];
  }

  /// Copy contents to a double[3] pointer
  void toD3(double dVec[3]) const
  {
    if (dVec == nullptr) return;
    dVec[0] = v_[0]; dVec[1] = v_[1]; dVec[2] = v_[2];
  }

  /// Set first element component
  void setV0(double value) { v_[0] = value; }
  /// Set second element component
  void setV1(double value) { v_[1] = value; }
  /// Set third element component
  void setV2(double value) { v_[2] = value; }

  /// Set all elements
  void set(double v0, double v1, double v2) { v_[0] = v0; v_[1] = v1; v_[2] = v2; }
  /// Set all elements
  void set(const Vec3& value) { v_[0] = value[0]; v_[1] = value[1]; v_[2] = value[2]; }

  /// Scales all elements
  void scale(double value) { v_[0] *= value; v_[1] *= value; v_[2] *= value; }
  ///@}

  /**@name Mappings for {x,y,z}, {yaw,pitch,roll}, etc.
   * @{
   */
  double x() const { return v_[0]; }
  double y() const { return v_[1]; }
  double z() const { return v_[2]; }

  double lat() const { return v_[0]; }
  double lon() const { return v_[1]; }
  double alt() const { return v_[2]; }

  double range() const { return v_[0]; }
  double raeAz() const { return v_[1]; }
  double raeEl() const { return v_[2]; }

  double yaw() const { return v_[0]; }
  double pitch() const { return v_[1]; }
  double roll() const { return v_[2]; }

  double psi() const { return v_[0]; }
  double theta() const { return v_[1]; }
  double phi() const { return v_[2]; }

  void setX(double value) { setV0(value); }
  void setY(double value) { setV1(value); }
  void setZ(double value) { setV2(value); }

  void setLat(double value) { setV0(value); }
  void setLon(double value) { setV1(value); }
  void setAlt(double value) { setV2(value); }

  void setRange(double value) { setV0(value); }
  void setRaeAz(double value) { setV1(value); }
  void setRaeEl(double value) { setV2(value); }

  void setYaw(double value) { setV0(value); }
  void setPitch(double value) { setV1(value); }
  void setRoll(double value) { setV2(value); }

  void setPsi(double value) { setV0(value); }
  void setTheta(double value) { setV1(value); }
  void setPhi(double value) { setV2(value); }
  ///@}

  /**@name Convenience operator overrides
   * @{
   */

  /** Add vectors */
  Vec3 operator+(const Vec3& r) const { return Vec3(x() + r.x(), y() + r.y(), z() + r.z()); }
  /** Add and assign vector */
  Vec3& operator+=(const Vec3& r) { set(x() + r.x(), y() + r.y(), z() + r.z()); return *this; }

  /** Subtract vectors */
  Vec3 operator-(const Vec3& r) const { return Vec3(x() - r.x(), y() - r.y(), z() - r.z()); }
  /** Subtract and assign vector */
  Vec3& operator-=(const Vec3& r) { set(x() - r.x(), y() - r.y(), z() - r.z()); return *this; }

  /** Scale by a value */
  Vec3 operator*(double scalar) const { return Vec3(x() * scalar, y() * scalar, z() * scalar); }
  /** Scale by a value and assign vector */
  Vec3& operator*=(double scalar) { set(x() * scalar, y() * scalar, z() * scalar); return *this; }

  /** Divide by a value */
  Vec3 operator/(double divisor) const {
    if (divisor == 0.) return Vec3();
    return Vec3(x() / divisor, y() / divisor, z() / divisor);
  }
  /** Divide by a value and assign vector */
  Vec3& operator/=(double divisor) {
    if (divisor == 0.)
      zero();
    else
      set(x() / divisor, y() / divisor, z() / divisor);
    return *this;
  }

  /** Negate vector */
  Vec3 operator-() const { return Vec3(-x(), -y(), -z()); }

  ///@}

  /** Calculates the dot product of this vector and another vector */
  double dot(const Vec3& r) const { return x() * r.x() + y() * r.y() + z() * r.z(); }
  /** Calculates the cross product of this vector and another vector */
  Vec3 cross(const Vec3& r) const {
    return Vec3(v_[1] * r.v_[2] - v_[2] * r.v_[1],
      v_[2] * r.v_[0] - v_[0] * r.v_[2],
      v_[0] * r.v_[1] - v_[1] * r.v_[0]);
  }
  /** Calculates the length of the vector */
  double length() const { return std::sqrt(x() * x() + y() * y() + z() * z()); }
  /** Calculates the unit length of the vector by normalizing; values within specified tolerance are set to 0. */
  Vec3 normalize(double t = 1.0e-9) const {
    const double len = length();
    if (len == 0.)
      return Vec3();
    Vec3 rv = *this;
    rv /= len;
    if (std::fabs(rv.x()) < t) rv.setX(0.);
    if (std::fabs(rv.y()) < t) rv.setY(0.);
    if (std::fabs(rv.z()) < t) rv.setZ(0.);
    return rv;
  }

private:
  double v_[3] = { 0., 0., 0. };
};

} // namespace simCore

#endif /* SIMCORE_CALC_VEC3_H */

i think i shoud define my own serialization and deserialization like this, https://github.com/jorgen/json_struct/blob/master/examples/08_custom_types.cpp

and got this

namespace JS {
    template<>
    struct TypeHandler<simCore::Vec3>
    {
    public:
        static inline Error to(simCore::Vec3& to_type, ParseContext& context)
        {
            char* pointer;
            unsigned long value = strtoul(context.token.value.data, &pointer, 10);
            to_type.setX(0);
            to_type.setY(0);
            to_type.setZ(0);
            return Error::NoError;
        }

        static void from(const simCore::Vec3& from_type, Token& token, Serializer& serializer)
        {
            std::string buf = "{"
                + std::to_string(from_type.x())
                + std::to_string(from_type.y())
                + std::to_string(from_type.z())
                + "}";
            token.value_type = Type::ObjectEnd;
            token.value.data = buf.data();
            token.value.size = buf.size();
            serializer.write(token);
        }
    };
}

but the compiler can NOT accept this code. i wonder why ?

jorgen commented 6 months ago

This worked for me, but the typehandler was not generating the correct data. I made a typehandler that handles the vec3 as a json array and not an object. Does this work for you? Godbolt

namespace JS {
    template<>
    struct TypeHandler<simCore::Vec3>
    {
    public:
        static inline Error to(simCore::Vec3& to_type, ParseContext& context)
        {
            double array[3];
            auto err = TypeHandler<double[3]>::to(array, context);
            if (err != Error::NoError)
                return err;

            to_type.setX(array[0]);
            to_type.setY(array[1]);
            to_type.setZ(array[2]);
            return Error::NoError;
        }

        static void from(const simCore::Vec3& from_type, Token& token, Serializer& serializer)
        {
            double array[3];
            array[0] = from_type.x();
            array[1] = from_type.y();
            array[2] = from_type.z();
            TypeHandler<double[3]>::from(array, token, serializer);
        }
    };
}
yanzixiang commented 5 months ago

YES,THANKS.

This worked for me, but the typehandler was not generating the correct data. I made a typehandler that handles the vec3 as a json array and not an object. Does this work for you? Godbolt

namespace JS {
    template<>
    struct TypeHandler<simCore::Vec3>
    {
    public:
        static inline Error to(simCore::Vec3& to_type, ParseContext& context)
        {
            double array[3];
            auto err = TypeHandler<double[3]>::to(array, context);
            if (err != Error::NoError)
                return err;

            to_type.setX(array[0]);
            to_type.setY(array[1]);
            to_type.setZ(array[2]);
            return Error::NoError;
        }

        static void from(const simCore::Vec3& from_type, Token& token, Serializer& serializer)
        {
            double array[3];
            array[0] = from_type.x();
            array[1] = from_type.y();
            array[2] = from_type.z();
            TypeHandler<double[3]>::from(array, token, serializer);
        }
    };
}