SERIALIZED_{FIELDS,VALUES} incorrectly require public constructors

Issue #356 resolved
Dan Bonachea created an issue

From spec 6.2.1 (UPCXX_SERIALIZED_FIELDS):

The class must have a default constructor; the default constructor may have any access level.

From spec 6.2.2 (UPCXX_SERIALIZED_VALUES):

T must have a constructor that can be invoked with the resulting rvalues from the body of a static member function

This design was chosen to allow serialization to use private constructors, avoiding the need to expose those construction routes to general clients of a class. This enables streamlined serialization constructors that (for example) bypass input enforcement or skip other processing that may need to occur to maintain invariants when a new object is instantiated from thin air. Another important motivation for this design is it enables the use of UPCXX_SERIALIZED_FIELDS in classes that semantically should not appear DefaultConstructible to their clients.

Unfortunately, this detail appears to be implemented incorrectly in the 2020.3.0 release. The code below demonstrates that the current implementations of both UPCXX_SERIALIZED_FIELDS and UPCXX_SERIALIZED_VALUES appear to rely upon the constructor invoked during deserialzation having a public access level.

#include <upcxx/upcxx.hpp>
#include <iostream>
#include <assert.h>

class FieldPoint {
private:
 double x;
 double y;

public:
 double get_x() { return x; }
 double get_y() { return y; }

 UPCXX_SERIALIZED_FIELDS(x,y) // "must be invoked...(at) public access level"

 static FieldPoint factory(double x, double y) {
   FieldPoint fp;
   fp.x = x;
   fp.y = y;
   return fp;
 }
#ifndef WORKAROUND1
private:
#endif
 FieldPoint() : x(0), y(0) {} // "default constructor may have any access level"
};

class Point {
private:
 double x;
 double y;

public:
 double get_x() { return x; }
 double get_y() { return y; }

 UPCXX_SERIALIZED_VALUES(float(x), float(y)) // "must be invoked...(at) public access level"

 // "must have a constructor that can be invoked with the resulting rvalues from the body of a static member function"
 static Point factory(float x, float y) {
   Point p(x,y);
   return p;
 }

#ifndef WORKAROUND2
private:
#endif
 Point(float a, float b) : x(a), y(b) {} // the only constructor
};

int main() {
  upcxx::init();

  std::cout<<"Hello from "<<upcxx::rank_me()<<" of "<<upcxx::rank_n()<<"\n";
  upcxx::barrier();
  double v1 = 100+upcxx::rank_me(), v2 = 1000+upcxx::rank_me();

  FieldPoint fp = FieldPoint::factory(v1, v2);
  assert(fp.get_x() == v1 && fp.get_y() == v2);

#ifndef DISABLE_SF
  upcxx::rpc(0, [](FieldPoint p) { std::cout << p.get_x() << ", " << p.get_y() << std::endl; }, fp).wait();
#endif

  upcxx::barrier();

  Point p = Point::factory(v1, v2);
  assert(p.get_x() == v1 && p.get_y() == v2);

#ifndef DISABLE_SV
  upcxx::rpc(0, [](Point lp) { std::cout << lp.get_x() << ", " << lp.get_y() << std::endl; }, p).wait();
#endif

  upcxx::barrier();
  if (!upcxx::rank_me()) std::cout<<"SUCCESS"<<std::endl;
  upcxx::finalize();
  return 0;
}

When the relevant constructors are declared private (compiling with no defines) I see the following type errors:

$ upcxx -g -network=smp ser-private.cpp 
In file included from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/bind.hpp:6,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/team_fwd.hpp:4,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/team.hpp:4,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/backend.hpp:7,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/allocate.hpp:8,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/upcxx.hpp:17,
                 from ser-private.cpp:1:
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp: In instantiation of 'static upcxx::detail::serialization_fields<T>::deserialized_type* upcxx::detail::serialization_fields<T>::deserialize(Reader&, void*) [with Reader = upcxx::detail::serialization_reader; T = FieldPoint; upcxx::detail::serialization_fields<T>::deserialized_type = FieldPoint]':
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:610:68:   required from 'T1* upcxx::detail::serialization_reader::read_into(void*) [with T = FieldPoint; T1 = FieldPoint]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:1409:14:   required from 'static TupOut* upcxx::detail::serialization_tuple<std::tuple<_Tps ...>, i, n>::deserialize_each(Reader&, void*, Ptrs ...) [with TupOut = std::tuple<FieldPoint>; Reader = upcxx::detail::serialization_reader; Ptrs = {}; T = {FieldPoint}; int i = 0; int n = 1]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:1462:105:   required from 'static upcxx::serialization<std::tuple<_Tps ...> >::deserialized_type* upcxx::serialization<std::tuple<_Tps ...> >::deserialize(Reader&, void*) [with Reader = upcxx::detail::serialization_reader; T = {FieldPoint}; upcxx::serialization<std::tuple<_Tps ...> >::deserialized_type = std::tuple<FieldPoint>]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:610:68:   required from 'T1* upcxx::detail::serialization_reader::read_into(void*) [with T = std::tuple<FieldPoint>; T1 = std::tuple<FieldPoint>]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/bind.hpp:277:7:   required from 'static upcxx::serialization<upcxx::bound_function<Fn, B ...> >::deserialized_type* upcxx::serialization<upcxx::bound_function<Fn, B ...> >::deserialize(Reader&, void*) [with Reader = upcxx::detail::serialization_reader; Fn = main()::<lambda(FieldPoint)>; B = {FieldPoint}; upcxx::serialization<upcxx::bound_function<Fn, B ...> >::deserialized_type = upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint>; typename upcxx::binding<T>::off_wire_type = main()::<lambda(FieldPoint)>]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:610:68:   [ skipping 6 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/command.hpp:70:30:   required from 'static void upcxx::detail::command<Arg>::serialize(Writer&, std::size_t, Fn1&&) [with upcxx::detail::serialization_reader (* reader)(Arg ...) = upcxx::backend::gasnet::rpc_as_lpc::reader_of; void (* cleanup)(Arg ...) = upcxx::backend::gasnet::rpc_as_lpc::cleanup<true, false>; Fn1 = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(FieldPoint)>&&; Arg = {FieldPoint&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint> >&&)>, upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint> >&; Writer = upcxx::detail::serialization_writer<true>; Fn = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(FieldPoint)>&&; Arg = {FieldPoint&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint> >&&)>, upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint> >; Arg = {upcxx::detail::lpc_base*}; std::size_t = long unsigned int]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/backend/gasnet/runtime.hpp:407:8:   required from 'upcxx::backend::gasnet::am_send_buffer<decltype (upcxx::detail::command<upcxx::detail::lpc_base*>::ubound<Fn1, SS, Fn>(upcxx::empty_storage_size, fn))> upcxx::backend::prepare_am(Fn&&, std::size_t, std::integral_constant<bool, any>) [with Fn = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(FieldPoint)>&&; Arg = {FieldPoint&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint> >&&)>, upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint> >; bool restricted = false; decltype (upcxx::detail::command<upcxx::detail::lpc_base*>::ubound<Fn1, SS, Fn>(upcxx::empty_storage_size, fn)) = upcxx::storage_size<40, 8>; std::size_t = long unsigned int]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/backend/gasnet/runtime.hpp:454:41:   required from 'void upcxx::backend::send_am_master(const upcxx::team&, upcxx::intrank_t, Fn&&) [with upcxx::progress_level level = upcxx::progress_level::user; Fn = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(FieldPoint)>&&; Arg = {FieldPoint&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint> >&&)>, upcxx::bound_function<main()::<lambda(FieldPoint)>, FieldPoint> >; upcxx::intrank_t = int]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/rpc.hpp:231:61:   required from 'typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(FieldPoint)>&&; Arg = {FieldPoint&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/rpc.hpp:295:92:   required from 'typename upcxx::detail::rpc_return<Fn(Arg ...), upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> > >::type upcxx::rpc(upcxx::intrank_t, Fn&&, Arg&& ...) [with Fn = main()::<lambda(FieldPoint)>; Arg = {FieldPoint&}; typename upcxx::detail::rpc_return<Fn(Arg ...), upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> > >::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]'
ser-private.cpp:62:99:   required from here
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:908:18: error: 'FieldPoint::FieldPoint()' is private within this context
  908 |         T *rec = ::new(raw) T;
      |                  ^~~~~~~~~~~~
ser-private.cpp:25:2: note: declared private here
   25 |  FieldPoint() : x(0), y(0) {} // "default constructor may have any access level"
      |  ^~~~~~~~~~
In file included from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/bind.hpp:6,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/team_fwd.hpp:4,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/team.hpp:4,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/backend.hpp:7,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/allocate.hpp:8,
                 from /usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/upcxx.hpp:17,
                 from ser-private.cpp:1:
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp: In instantiation of 'static Obj* upcxx::detail::serialization_values_each<TupRefs, n, n>::deserialize(Reader&, void*, Ptrs ...) [with Obj = Point; Reader = upcxx::detail::serialization_reader; Ptrs = {float*, float*}; TupRefs = std::tuple<float, float>; int n = 2]':
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:989:59:   required from 'static Obj* upcxx::detail::serialization_values_each<TupRefs, i, n>::deserialize(Reader&, void*, Ptrs ...) [with Obj = Point; Reader = upcxx::detail::serialization_reader; Ptrs = {}; TupRefs = std::tuple<float, float>; int i = 0; int n = 2]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:1053:81:   required from 'static upcxx::detail::serialization_values<T>::deserialized_type* upcxx::detail::serialization_values<T>::deserialize(Reader&, void*) [with Reader = upcxx::detail::serialization_reader; T = Point; upcxx::detail::serialization_values<T>::deserialized_type = Point]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:610:68:   required from 'T1* upcxx::detail::serialization_reader::read_into(void*) [with T = Point; T1 = Point]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:1409:14:   required from 'static TupOut* upcxx::detail::serialization_tuple<std::tuple<_Tps ...>, i, n>::deserialize_each(Reader&, void*, Ptrs ...) [with TupOut = std::tuple<Point>; Reader = upcxx::detail::serialization_reader; Ptrs = {}; T = {Point}; int i = 0; int n = 1]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:1462:105:   [ skipping 8 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/command.hpp:35:44:   required from 'static void upcxx::detail::command<Arg>::the_executor(Arg ...) [with Fn = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(Point)>&&; Arg = {Point&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(Point)>, Point> >&&)>, upcxx::bound_function<main()::<lambda(Point)>, Point> >; upcxx::detail::serialization_reader (* reader)(Arg ...) = upcxx::backend::gasnet::rpc_as_lpc::reader_of; void (* cleanup)(Arg ...) = upcxx::backend::gasnet::rpc_as_lpc::cleanup<true, false>; Arg = {upcxx::detail::lpc_base*}]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/command.hpp:70:30:   required from 'static void upcxx::detail::command<Arg>::serialize(Writer&, std::size_t, Fn1&&) [with upcxx::detail::serialization_reader (* reader)(Arg ...) = upcxx::backend::gasnet::rpc_as_lpc::reader_of; void (* cleanup)(Arg ...) = upcxx::backend::gasnet::rpc_as_lpc::cleanup<true, false>; Fn1 = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(Point)>&&; Arg = {Point&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(Point)>, Point> >&&)>, upcxx::bound_function<main()::<lambda(Point)>, Point> >&; Writer = upcxx::detail::serialization_writer<true>; Fn = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(Point)>&&; Arg = {Point&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(Point)>, Point> >&&)>, upcxx::bound_function<main()::<lambda(Point)>, Point> >; Arg = {upcxx::detail::lpc_base*}; std::size_t = long unsigned int]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/backend/gasnet/runtime.hpp:407:8:   required from 'upcxx::backend::gasnet::am_send_buffer<decltype (upcxx::detail::command<upcxx::detail::lpc_base*>::ubound<Fn1, SS, Fn>(upcxx::empty_storage_size, fn))> upcxx::backend::prepare_am(Fn&&, std::size_t, std::integral_constant<bool, any>) [with Fn = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(Point)>&&; Arg = {Point&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(Point)>, Point> >&&)>, upcxx::bound_function<main()::<lambda(Point)>, Point> >; bool restricted = false; decltype (upcxx::detail::command<upcxx::detail::lpc_base*>::ubound<Fn1, SS, Fn>(upcxx::empty_storage_size, fn)) = upcxx::storage_size<32, 8>; std::size_t = long unsigned int]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/backend/gasnet/runtime.hpp:454:41:   required from 'void upcxx::backend::send_am_master(const upcxx::team&, upcxx::intrank_t, Fn&&) [with upcxx::progress_level level = upcxx::progress_level::user; Fn = upcxx::bound_function<upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(Point)>&&; Arg = {Point&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]::<lambda(upcxx::deserialized_type_t<upcxx::bound_function<main()::<lambda(Point)>, Point> >&&)>, upcxx::bound_function<main()::<lambda(Point)>, Point> >; upcxx::intrank_t = int]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/rpc.hpp:231:61:   required from 'typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type upcxx::detail::rpc(const upcxx::team&, upcxx::intrank_t, Cxs, Fn&&, Arg&& ...) [with Cxs = upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> >; Fn = main()::<lambda(Point)>&&; Arg = {Point&}; typename upcxx::detail::rpc_return<Fn(Arg ...), Cxs>::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]'
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/rpc.hpp:295:92:   required from 'typename upcxx::detail::rpc_return<Fn(Arg ...), upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> > >::type upcxx::rpc(upcxx::intrank_t, Fn&&, Arg&& ...) [with Fn = main()::<lambda(Point)>; Arg = {Point&}; typename upcxx::detail::rpc_return<Fn(Arg ...), upcxx::completions<upcxx::future_cx<upcxx::operation_cx_event, upcxx::progress_level::user> > >::type = upcxx::future1<upcxx::detail::future_kind_shref<upcxx::detail::future_header_ops_promise> >; upcxx::intrank_t = int]'
ser-private.cpp:71:96:   required from here
/usr/local/pkg/upcxx-dirac/gcc-9.3.0/stable-2020.3.0/upcxx.debug.gasnet_seq.smp/include/upcxx/serialization.hpp:1018:16: error: 'Point::Point(float, float)' is private within this context
 1018 |         return ::new(spot) Obj(static_cast<typename std::remove_pointer<Ptrs>::type&&>(*ptrs)...);
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ser-private.cpp:48:2: note: declared private here
   48 |  Point(float a, float b) : x(a), y(b) {} // the only constructor
      |  ^~~~~

The workararound is to declare the relevant constructors public (-DWORKAROUND1 -DWORKAROUND2), however this of course defeats the access-control/abstraction benefits of using a private constructor that our design was trying to allow.

Comments (8)

  1. john bachan

    This fixes issue 356 where non-public constructors were inaccessible to UCPXX_SERIALIZED_XXX macros. The fix entails smuggling a public static function into the class that then calls the constructor.

    → <<cset 561af4abe6d2>>

  2. Dan Bonachea reporter

    Merging release upcxx-2020.3.2

    • upcxx-2020.3: ChangeLog: update release date 2020.3.2 package version bumps Make future and serialization tests "full" upc++ ChangeLog cleanups Update ChangeLog
    • Added test/regression/issue380.cpp - Fixed issue 380 where rput with source+operation cx failed to compile. Fix issue 368. Needed to add a default constructor for bcast_payload_header in runtime.hpp since it has a union containing a non-trivially constructible member (std::atomic). Add a Serialization example with an abstract base class ChangeLog.md bugfix entry Bugfix issue 369. <completion>::as_future() was typically leaking a small 56 byte heap struct per completion notification due to a refcount bungling in detail::persona_tls::fulfill_during_user_of_active(). configure: search PATH for python3 and python2 configure: implement check_tool_path() Update ChangeLog Add issue371 test Fix issue 371: team_id's are not "universal" as documented Update ChangeLog Add issue343 test fix issue 343: ensure that default-construced team_id provide a unique invalid team_id Update ChangeLog Improve bad_shared_alloc to output shared heap status Add a test to demonstrate shared heap allocation failures Tweak shared heap allocation exceptions test/memberof.cpp: silence a harmless warning with -std=c++2a docs: revise Intel/libstdc++ recommendation bench/misc_perf: avoid C++20 deprecation warnings INSTALL.md: Clarify recommendations for using parallel make ChangeLog.md: list fix #356 Fully qualified std::foo to ::std::foo in UPCXX_SERIALIZED_FOO macros. For members injected into user classes by UPCXX_SERIALIZED_* macros: This fixes issue 356 where non-public constructors were inaccessible to UCPXX_SERIALIZED_XXX macros. The fix entails smuggling a public static function into the class that then calls the constructor. Add issue356 regression test Update ChangeLog ChangeLog: document CROSS to UPCXX_CROSS rename configure: automatically cross-compile on Cray XC Start a ChangeLog for hotfix release

    → <<cset e35f012d9670>>

  3. Dan Bonachea reporter

    Merging release upcxx-2020.3.2

    • upcxx-2020.3: ChangeLog: update release date 2020.3.2 package version bumps Make future and serialization tests "full" upc++ ChangeLog cleanups Update ChangeLog
    • Added test/regression/issue380.cpp - Fixed issue 380 where rput with source+operation cx failed to compile. Fix issue 368. Needed to add a default constructor for bcast_payload_header in runtime.hpp since it has a union containing a non-trivially constructible member (std::atomic). Add a Serialization example with an abstract base class ChangeLog.md bugfix entry Bugfix issue 369. <completion>::as_future() was typically leaking a small 56 byte heap struct per completion notification due to a refcount bungling in detail::persona_tls::fulfill_during_user_of_active(). configure: search PATH for python3 and python2 configure: implement check_tool_path() Update ChangeLog Add issue371 test Fix issue 371: team_id's are not "universal" as documented Update ChangeLog Add issue343 test fix issue 343: ensure that default-construced team_id provide a unique invalid team_id Update ChangeLog Improve bad_shared_alloc to output shared heap status Add a test to demonstrate shared heap allocation failures Tweak shared heap allocation exceptions test/memberof.cpp: silence a harmless warning with -std=c++2a docs: revise Intel/libstdc++ recommendation bench/misc_perf: avoid C++20 deprecation warnings INSTALL.md: Clarify recommendations for using parallel make ChangeLog.md: list fix #356 Fully qualified std::foo to ::std::foo in UPCXX_SERIALIZED_FOO macros. For members injected into user classes by UPCXX_SERIALIZED_* macros: This fixes issue 356 where non-public constructors were inaccessible to UCPXX_SERIALIZED_XXX macros. The fix entails smuggling a public static function into the class that then calls the constructor. Add issue356 regression test Update ChangeLog ChangeLog: document CROSS to UPCXX_CROSS rename configure: automatically cross-compile on Cray XC Start a ChangeLog for hotfix release

    → <<cset e35f012d9670>>

  4. Log in to comment