Skip to content

v1_0_cpp_versioning

Takatoshi Kondo edited this page Apr 3, 2015 · 2 revisions

API versioning on C++

msgpack-c have introduced API versioning mechanism.

Macros

MSGPACK_DEFAULT_API_VERSION

MSGPACK_DEFAULT_API_VERSION means the default version of msgpack-c API. The current value is 1.

#if !defined(MSGPACK_DEFAULT_API_VERSION)
#define MSGPACK_DEFAULT_API_VERSION 1
#endif

When you want to set MSGPACK_API_VERSION explicitly, give the macro value as follows:

g++ -DMSGPACK_API_VERSION=2

MSGPACK_DEFAULT_API_NS

MSGPACK_DEFAULT_API_NS is a namespace name of the default msgpack version. It names 'v' + MSGPACK_DEFAULT_API_VERSION. e.g.) v1, v2, ...

It is used when you declare and define your custom overloads of operators.

See the following example:

https://github.com/msgpack/msgpack-c/blob/cpp-1.0.1/example/cpp03/class_non_intrusive.cpp#L28

https://github.com/msgpack/msgpack-c/blob/cpp-1.0.1/example/cpp03/class_non_intrusive.cpp#L67

MSGPACK_API_VERSION_NAMESPACE

MSGPACK_API_VERSION_NAMESPACE is a function style macro. It defines a namespace. You can pass an argument as the namespace you want to define.

MSGPACK_API_VERSION_NAMESPACE(v1) {
   // ...
}

MSGPACK_API_VERSION_NAMESPACE(v2) {
   // ...
}

The macro is expanded to namespace. If the given namespace name is same as MSGPACK_DEFAULT_API_NS, the macro is expanded as follows:

In the case MSGPACK_DEFAULT_API_NS == v1

C++11

inline namespace v1 {
   // ...
}

namespace v2 {
   // ...
}

C++03

namespace v1{}; using namespace v1; namespace v1 {
   // ...
}

namespace v2 {
   // ...
}

It is used when you declare and define your custom overloads of operators.

See the following example:

https://github.com/msgpack/msgpack-c/blob/cpp-1.0.1/example/cpp03/class_non_intrusive.cpp#L28

https://github.com/msgpack/msgpack-c/blob/cpp-1.0.1/example/cpp03/class_non_intrusive.cpp#L67

Overloading operators

When you want to adapt your class to msgpack-c, you have two choice. One is intrusive approach, the other is non-intrusive approach.

Intrusive approach

You just add MSGPACK_DEFINE macro on the public are of your class.

// Both ok.
#include <msgpack_fwd.hpp>
// or
#include <msgpack.hpp>

class your_class {
public:
    your_class() {} // The class needs to be default constructible
                    // when you want to convert from msgpack::object
                    // to your_class.
    your_class(int mem1, std::string const& mem2)
        :mem1_(mem1), mem2_(mem2) {}

private:
    // Data members can be private.
    int mem1_;
    std::string mem2_;

public:
    // Define the following macro as public:
    MSGPACK_DEFINE(mem1_, mem2_);
};

// When you include msgpack_fwd.hpp above, you need to include msgpack.h
// somewhere.
#include <msgpack.hpp>

Non-intrusive approach

Sometimes you can't modify the class you want to adapt msgpack-c. For example the class is a part of the third party library. You need to declrare and define operators as the following mannar:

// Include msgpack_fwd.hpp before your overloads are declared.
// Don't include msgpack.hpp here.
#include <msgpack_fwd.hpp>

// Declare the target class.
// Or include third_class_fwd.hpp if it's provided.
class third_class;

namespace msgpack {
MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {

// Declare operators

// For convert third_class form msgpack::object.
object const& operator>> (msgpack::object const& o, third_class& v);


// For pack.
template <typename Stream>
packer<Stream>& operator<< (msgpack::packer<Stream>& o, third_class const& v);

// For create object::with_zone from third_class.
void operator<< (msgpack::object::with_zone& o, const third_class& v);


} // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
} // namespace msgpack


// Define third_class or include third_class.hpp.
class third_class {
public:
    third_class() {} // The class needs to be default constructible
                     // when you want to convert from msgpack::object
                     // to third_class.
    third_class(int mem1, std::string const& mem2)
        :mem1_(mem1), mem2_(mem2) {}

    // The class need to provide getter functions that return
    // the values you want to pack.
    int get_mem1() const { return mem1_; }
    std::string get_mem2() const { return mem2_; }

private:
    // Data members can be private.
    int mem1_;
    std::string mem2_;
};

// include msgpack.hpp after your overloads declared.
#include <msgpack.hpp>

namespace msgpack {
MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {

// Declare operators after msgpack.hpp included

// For convert third_class form msgpack::object.
inline object const& operator>> (msgpack::object const& o, third_class& v) {
    if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
    if (o.via.array.size != 2) throw msgpack::type_error();
    v = third_class(
        o.via.array.ptr[0].as<int>(),
        o.via.array.ptr[1].as<std::string>());
    return o;
}


// For pack.
template <typename Stream>
inline packer<Stream>& operator<< (msgpack::packer<Stream>& o, third_class const& v) {
    // packing member variables as an array.
    o.pack_array(2);
    o.pack(v.get_mem1());
    o.pack(v.get_mem2());
    return o;
}

// For create object::with_zone from third_class.
inline void operator<< (msgpack::object::with_zone& o, const third_class& v) {
    o.type = type::ARRAY;
    msgpack::object* p = static_cast<object*>(o.zone.allocate_align(sizeof(msgpack::object)*2));
    o.via.array.ptr = p;
    o.via.array.size = 2;
    p[0] = msgpack::object(v.get_mem1(), o.zone);
    p[1] = msgpack::object(v.get_mem1(), o.zone);
}

} // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
} // namespace msgpack

You only define operator<< (msgpack::object::with_zone& o, const third_class& v) overload only when you want to use msgpack::object::with_zone.

Implementation note

See this discussion.