1

I've a class that one of its members is a pointer to a 2d array of integers. I want to pass this member in serialize function, but I'm getting this error: error: request for member ‘serialize’ in ‘t’, which is of non-class type ‘int’ I read the documentation and I have found that they have used

ar.register_type(static_cast<type *>(NULL));  

so I did the same thing, but still I have a problem.

I'm attaching the piece of code that I'm trying to serialize:

#include <iostream>
#include<set>
#include<map>
#include <boost/mpi/datatype.hpp>
#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
#include <boost/mpi.hpp> 

class Dummy 
{
private: 
    int m_N;
    int** array; 
    friend class boost::serialization::access;
    template <class Archive>
    void serialize(Archive &ar, const unsigned int /*version*/)
    {
        ar &m_N; 
        ar.register_type(static_cast<int**>(NULL));
        ar &array; 
    }
public:
    Dummy(){}; 
    Dummy(const int N) 
    {
        m_N=N; 

        array = new int*[N+1];
        for(auto i=0;i<N+1;i++)
        {
            array[i] = new int[N+1];
        }
    };
    ~Dummy()
    {
        for(auto i=0;i<m_N+1;i++)
        {
            delete[]array[i];
        }delete[]array;
    }; 
    
};
namespace boost { namespace mpi {
    template <>
struct is_mpi_datatype<Dummy> : mpl::true_ { };
} }

int main(int argc, char* const argv[]) 
{
    boost::mpi::environment   env; 
    boost::mpi::communicator  world; 
    std::map<int,std::vector<Dummy>> IdToVecObject;
    std::vector<Dummy> vec; 
    std::set<int> neigbours; 
    if(world.rank()==0)
    {   
        Dummy obj(1); 
        vec.push_back(obj); 
        neigbours.insert(1); 
        IdToVecObject.emplace(1,vec); 

    }
    if(world.rank()==1)
    {
        Dummy obj(1); 
        vec.push_back(obj); 
        neigbours.insert(0); 
        IdToVecObject.emplace(0,vec); 
    }
    for(auto it:IdToVecObject)
    {
        int target= it.first;
        Dummy obj(1); 
        world.send(target,0,obj); 
    }
    for(auto i:neigbours)
    {
        int source= i ;  
        Dummy obj(1); 
        std::vector<Dummy> objects; 
        world.recv(source,0,obj); 
    }
    return 0; 
}
MA19
  • 510
  • 3
  • 15

1 Answers1

1

Your array is not a 2-dimensional array. At most it could be described as a "regular" jagged array.

register_type relates only to types in a polymorphic type hierarchy, so that's completely unrelated to C arrays, or int for that matter.

You need to serialize the elements. The simplest way to do it:

template <class Archive> void serialize(Archive& ar, unsigned) {
    ar& m_N;
    for (int i = 0; i<m_N; ++i)
        ar& boost::serialization::make_array(array[i], m_N);
}

WHOOPS

I forgot to manually manage the allocations - which is exactly why you don't use C arrays:

class Dummy {
  private:
    int   m_N     = 0;
    int** m_array = nullptr;

    friend class boost::serialization::access;

    template <class Archive> void serialize(Archive& ar, unsigned) {
        if /*constexpr*/ (Archive::is_saving) {
            ar& m_N;
        } else {
            int curN = m_N;
            ar& m_N;

            if (m_N > curN) // keep old allocation if big enough, optionally
                init();
        }

        for (auto* el : m_array)
            ar & boost::serialization::make_array(el, m_N);
    }

    void init() {
        if (m_array)
            destroy();
        m_array = new int*[m_N + 1];
        for (auto& el : m_array)
            el = new int[m_N + 1];
    }
    void destroy() {
        for (auto& el : m_array)
            delete[] el;
        delete[] m_array;
        m_array = nullptr;
    }

  public:
    Dummy(int N = 0) : m_N(N){ init(); };
    ~Dummy() { destroy(); };
};

(you may need to #include <boost/serialization/array_wrapper.hpp>).

The much saner way to do it, would be to actually use a multidimensional array: https://stackoverflow.com/a/62109089/85371 or at least store them contiguously like int[N][M] or even simpler int[N*M].

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Added the manual allocation fest that belonged with the T** approach. Just use something better :) – sehe Jun 28 '23 at 10:31
  • Thanks a lot for answer! One thing, this `m_array = new int**[m_N + 1]` should be changed to `m_array = new int*[m_N + 1]`. Also, I have included `` and `using namespace std` but I'm getting this error: `‘begin’ was not declared in this scope; did you mean ‘std::begin’? for (auto& el : m_array)` – MA19 Jun 28 '23 at 17:18
  • You have to qualify it because ADL doesn't do anything for primitive types. Yet another reason to avoid C code. I'm sorry I didn't have the time to run things through a compiler earlier. You're probably best off here using raw loops like you did unless you substitute a proper container model. – sehe Jun 28 '23 at 17:24
  • I'm not forced to use c arrays. So, std::vector here will be better?It's important to use dynamic allocation in this app – MA19 Jun 28 '23 at 19:23
  • I gave you the options I'd use. Vector is definitely better – sehe Jun 28 '23 at 19:41