Click here to Skip to main content
15,891,248 members
Articles / Programming Languages / C++

RCF - Interprocess Communication for C++

Rate me:
Please Sign up or sign in to vote.
4.94/5 (147 votes)
25 Oct 2011CPOL20 min read 4.6M   8.4K   331  
A server/client IPC framework, using the C++ preprocessor as an IDL compiler.
//*****************************************************************************
// RCF - Remote Call Framework
// Copyright (c) 2005. All rights reserved.
// Developed by Jarl Lindrud.
// Contact: jlindrud@hotmail.com .
//*****************************************************************************

#ifndef INCLUDE_RCF_MARSHAL_HPP
#define INCLUDE_RCF_MARSHAL_HPP

#include <RCF/ClientStub.hpp>
#include <RCF/SerializationProtocol.hpp>
#include <RCF/MethodInvocation.hpp>
#include <RCF/Tools.hpp>
#include <RCF/util/Meta.hpp>

#include <SF/Tools.hpp> // FOR_EACH_PRIMITIVE_TYPE

#include <boost/mpl/not.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

// TODO: move code into Marshal.inl

namespace RCF {

    // Boost.Serialization treats pointers of primitive types differently than other types,
    // hence the following hack, which unfortunately overrides all the serialization protocols,
    // not just Boost.Serialization.

#define RCF_DEFINE_PRIMITIVE_POINTER_SERIALIZATION(type)                                                                                                \
    inline SerializationProtocolOut &operator<<(SerializationProtocolOut &out, const type *pt) { return out << *pt; }                                   \
    inline SerializationProtocolOut &operator<<(SerializationProtocolOut &out, type *const pt) { return out << *pt; }                                   \
    inline SerializationProtocolIn &operator>>(SerializationProtocolIn &in, type *&pt) { RCF_ASSERT(pt==NULL); pt = new type(); return in >> *pt; }

    SF_FOR_EACH_FUNDAMENTAL_TYPE( RCF_DEFINE_PRIMITIVE_POINTER_SERIALIZATION )
    RCF_DEFINE_PRIMITIVE_POINTER_SERIALIZATION(std::string)

#undef RCF_DEFINE_PRIMITIVE_POINTER_SERIALIZATION

    // Boost.Serialization handles smart pointers very clumsily, so we'll do those ourselves

    //#define RCF_SERIALIZE_REFCOUNTSMARTPTR(RefCountSmartPtr)                                            

#define RefCountSmartPtr boost::shared_ptr

    template<typename T>
    inline SerializationProtocolOut &operator<<(SerializationProtocolOut &out, const RefCountSmartPtr<T> *spt)
    {
        return out << *spt;
    }

    template<typename T>
    inline SerializationProtocolOut &operator<<(SerializationProtocolOut &out, RefCountSmartPtr<T> *const spt)
    {
        return out << *spt;
    }

    template<typename T>
    inline SerializationProtocolIn &operator>>(SerializationProtocolIn &in, RefCountSmartPtr<T> *&spt)
    {
        spt = new RefCountSmartPtr<T>();
        return in >> *spt;
    }

    template<typename T>
    inline SerializationProtocolOut &operator<<(SerializationProtocolOut &out, const RefCountSmartPtr<T> &spt)
    {
        return out << spt.get();
    }

    template<typename T>
    inline SerializationProtocolIn &operator>>(SerializationProtocolIn &in, RefCountSmartPtr<T> &spt)
    {
        T *pt = NULL;
        in >> pt;
        RefCountSmartPtr<T> *pspt = 
            in.getPointerContext().template get< RefCountSmartPtr<T> * >(pt);
        if (pspt == NULL)
        {
            spt = RefCountSmartPtr<T>(pt);
            in.getPointerContext().template set< RefCountSmartPtr<T> * >(&spt, pt);
        }
        else
        {
            spt = *pspt;
        }
        return in;
    }

#undef RefCountSmartPtr

    
    struct Void {};

    namespace IDL {

        class DoRequest 
        {
        public:
            DoRequest(ClientStub &stub, bool oneway, const std::string &subInterface, int fnId, SerializationProtocolOut &out)
            {
                stub.onPreCall();
                stub.connectTransport();
                out.reset(stub.getSerializationProtocol());
                out << RCF::MethodInvocationRequest(stub.getToken(), stub.getServerBindingName(), subInterface, fnId, oneway, false);
            }
        };

        template<typename T>
        class InParameter
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            InParameter(const T &t, SerializationProtocolOut &out) : t_() { out << t; }
            InParameter(SerializationProtocolIn &in) : t_() { in >> t_; }
            const T &get() { return t_; }
        private:
            T t_;
        };

        template<typename T>
        class InParameter<T *>
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            typedef typename boost::remove_const<T>::type U;
            InParameter(T *pt, SerializationProtocolOut &out) 
            { 
                out << pt; 
            }
            InParameter(SerializationProtocolIn &in)
            {
                U *pu = NULL;
                in >> pu;
                boost::shared_ptr<U> *ppu = in.getPointerContext().template get< boost::shared_ptr<U>* >(pu);
                if (ppu == NULL)
                {
                    pu_ = boost::shared_ptr<U>(pu);
                    in.getPointerContext().template set< boost::shared_ptr<U>* >(&pu_, pu);
                }
                else
                {
                    pu_ = *ppu;
                }
            }
            T *get() 
            { 
                return pu_.get(); 
            }
        private:
            boost::shared_ptr<U> pu_;
        };

        template<typename T>
        class InParameter<T &>
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            typedef typename boost::remove_const<T>::type U;
            InParameter(T &t, SerializationProtocolOut &out) 
            { 
                out << &t; 
            }
            InParameter(SerializationProtocolIn &in) 
            {
                U *pu = NULL;
                in >> pu;
                boost::shared_ptr<U> *ppu = in.getPointerContext().template get< boost::shared_ptr<U>* >(pu);
                if (ppu == NULL)
                {
                    pu_ = boost::shared_ptr<U>(pu);
                    in.getPointerContext().template set< boost::shared_ptr<U>* >(&pu_, pu);
                }
                else
                {
                    pu_ = *ppu;
                }
            }

            T &get() 
            { 
                return *pu_; 
            }
        private:
            boost::shared_ptr<U> pu_;
        };

        template<typename T>
        class OutParameter
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            OutParameter(const T &, SerializationProtocolIn &, bool) 
            {}
            template<typename V> OutParameter( InParameter< V > &, SerializationProtocolOut &) 
            {}
        };

        template<typename T>
        class OutParameter<T *>
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            OutParameter(const T *t, SerializationProtocolIn &in, bool oneway) 
            {}
            template<typename V> OutParameter( InParameter< V > &v, SerializationProtocolOut &out) 
            {}
        };

        template<typename T>
        class OutParameter<const T &>
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            OutParameter(const T &t, SerializationProtocolIn &in, bool oneway) 
            {
                RCF_UNUSED_VARIABLE(t);
                RCF_UNUSED_VARIABLE(in);
                RCF_UNUSED_VARIABLE(oneway);
            }
            template<typename V> OutParameter( InParameter< V > &v, SerializationProtocolOut &out) 
            {
                RCF_UNUSED_VARIABLE(v);
                RCF_UNUSED_VARIABLE(out);
            }
        };

        template<typename T>
        class OutParameter<T &>
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            OutParameter(T &t, SerializationProtocolIn &in, bool oneway) 
            {
                if (!oneway)
                {
                    in >> t; 
                }
            }
            template<typename V> OutParameter( InParameter< V > &v, SerializationProtocolOut &out) 
            { 
                out << v.get(); 
            }
        };

        template<typename T = Void>
        class OutReturnValue 
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            OutReturnValue(SerializationProtocolOut &out, const T &t)
            {
                out << RCF::MethodInvocationResponse(false); 
                out  << t;
            }
        };

        template<>
        class OutReturnValue<Void>
        {
        public:
            OutReturnValue(SerializationProtocolOut &out, int t)
            {
                RCF_UNUSED_VARIABLE(t);
                out << RCF::MethodInvocationResponse(false); 
            }
        };

        template<typename T = Void>
        class InReturnValue 
        {
        public:
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_pointer<T>::type >::value );
            BOOST_STATIC_ASSERT( boost::mpl::not_< typename boost::is_reference<T>::type >::value );
            InReturnValue(ClientStub &clientStub, SerializationProtocolIn &in, SerializationProtocolOut &out, bool oneway);
            T &get();
        private:
            T t_;
        };

        template<>
        class InReturnValue<Void> 
        {
        public:
            InReturnValue(ClientStub &clientStub, SerializationProtocolIn &in, SerializationProtocolOut &out, bool oneway);
            Void get() { return Void(); }
        };

    } // namespace IDL

    // ClientMarshal

    template<
        typename R, 
        typename A1 = Meta::Null, 
        typename A2 = Meta::Null, 
        typename A3 = Meta::Null, 
        typename A4 = Meta::Null,
        typename A5 = Meta::Null,
        typename A6 = Meta::Null>
    class ClientMarshal;

    // NB: using this instead of ScopeGuard,because Borland C++ is not triggering ScopeGuard at all.
    // At least with this class, it's triggered in debug builds (but not release apparently).
    class ConnectionResetGuard
    {
    public:
        ConnectionResetGuard(ClientStub &clientStub) :
            mClientStub(clientStub),
            mDismissed()
        {}

        void dismiss()
        {
            mDismissed = true;
        }

        ~ConnectionResetGuard()
        {
            if (!mDismissed)
            {
                try
                {
                    mClientStub.resetTransport();
                }
                catch(...)
                {}
            }
        }

    private:
        ClientStub &mClientStub;
        bool mDismissed;
    };

    template<typename R>
    class ClientMarshal<R, Meta::Null, Meta::Null, Meta::Null, Meta::Null, Meta::Null, Meta::Null>
    {
    public:
        R operator()(ClientStub &clientStub, RemoteCallSemantics rcs, const std::string &subInterface, int fnId) const
        {
            bool oneway = (Oneway == rcs);
            ConnectionResetGuard connectionResetGuard(clientStub);
            RCF::IDL::DoRequest(clientStub, oneway, subInterface, fnId, clientStub.out);
            RCF::IDL::InReturnValue< R > ret(clientStub, clientStub.in, clientStub.out, oneway);
            connectionResetGuard.dismiss();
            return ret.get();
        }
    };

    template<typename R, typename A1>
    class ClientMarshal<R,A1, Meta::Null, Meta::Null, Meta::Null, Meta::Null, Meta::Null>
    {
    public:
        R operator()(ClientStub &clientStub, RemoteCallSemantics rcs, const std::string &subInterface, int fnId, A1 a1) const
        {
            bool oneway = (Oneway == rcs);
            ConnectionResetGuard connectionResetGuard(clientStub);
            RCF::IDL::DoRequest(clientStub, oneway, subInterface, fnId, clientStub.out);
            RCF::IDL::InParameter< A1 >(a1, clientStub.out);
            RCF::IDL::InReturnValue< R > ret(clientStub, clientStub.in, clientStub.out, oneway);
            RCF::IDL::OutParameter< A1 >(a1, clientStub.in, oneway);
            connectionResetGuard.dismiss();
            return ret.get();
        }
    };

    template<typename R, typename A1, typename A2>
    class ClientMarshal<R,A1,A2, Meta::Null, Meta::Null, Meta::Null, Meta::Null >
    {
    public:
        R operator()(ClientStub &clientStub, RemoteCallSemantics rcs, const std::string &subInterface, int fnId, A1 a1, A2 a2) const
        {
            bool oneway = (Oneway == rcs);
            ConnectionResetGuard connectionResetGuard(clientStub);
            RCF::IDL::DoRequest(clientStub, oneway, subInterface, fnId, clientStub.out);
            RCF::IDL::InParameter< A1 >(a1, clientStub.out);
            RCF::IDL::InParameter< A2 >(a2, clientStub.out);
            RCF::IDL::InReturnValue< R > ret(clientStub, clientStub.in, clientStub.out, oneway);
            RCF::IDL::OutParameter< A1 >(a1, clientStub.in, oneway);
            RCF::IDL::OutParameter< A2 >(a2, clientStub.in, oneway);
            connectionResetGuard.dismiss();
            return ret.get();
        }
    };

    template<typename R, typename A1, typename A2, typename A3>
    class ClientMarshal<R,A1,A2,A3, Meta::Null, Meta::Null, Meta::Null>
    {
    public:
        R operator()(ClientStub &clientStub, RemoteCallSemantics rcs, const std::string &subInterface, int fnId, A1 a1, A2 a2, A3 a3) const
        {
            bool oneway = (Oneway == rcs);
            ConnectionResetGuard connectionResetGuard(clientStub);
            RCF::IDL::DoRequest(clientStub, oneway, subInterface, fnId, clientStub.out);
            RCF::IDL::InParameter< A1 >(a1, clientStub.out);
            RCF::IDL::InParameter< A2 >(a2, clientStub.out);
            RCF::IDL::InParameter< A3 >(a3, clientStub.out);
            RCF::IDL::InReturnValue< R > ret(clientStub, clientStub.in, clientStub.out, oneway);
            RCF::IDL::OutParameter< A1 >(a1, clientStub.in, oneway);
            RCF::IDL::OutParameter< A2 >(a2, clientStub.in, oneway);
            RCF::IDL::OutParameter< A3 >(a3, clientStub.in, oneway);
            connectionResetGuard.dismiss();
            return ret.get();
        }
    };

    template<typename R, typename A1, typename A2, typename A3, typename A4>
    class ClientMarshal<R,A1,A2,A3,A4, Meta::Null, Meta::Null>
    {
    public:
        R operator()(ClientStub &clientStub, RemoteCallSemantics rcs, const std::string &subInterface, int fnId, A1 a1, A2 a2, A3 a3, A4 a4) const
        {
            bool oneway = (Oneway == rcs);
            ConnectionResetGuard connectionResetGuard(clientStub);
            RCF::IDL::DoRequest(clientStub, oneway, subInterface, fnId, clientStub.out);
            RCF::IDL::InParameter< A1 >(a1, clientStub.out);
            RCF::IDL::InParameter< A2 >(a2, clientStub.out);
            RCF::IDL::InParameter< A3 >(a3, clientStub.out);
            RCF::IDL::InParameter< A4 >(a4, clientStub.out);
            RCF::IDL::InReturnValue< R > ret(clientStub, clientStub.in, clientStub.out, oneway);
            RCF::IDL::OutParameter< A1 >(a1, clientStub.in, oneway);
            RCF::IDL::OutParameter< A2 >(a2, clientStub.in, oneway);
            RCF::IDL::OutParameter< A3 >(a3, clientStub.in, oneway);
            RCF::IDL::OutParameter< A4 >(a4, clientStub.in, oneway);
            connectionResetGuard.dismiss();
            return ret.get();
        }
    };

    template<typename R, typename A1, typename A2, typename A3, typename A4, typename A5>
    class ClientMarshal<R,A1,A2,A3,A4,A5, Meta::Null>
    {
    public:
        R operator()(ClientStub &clientStub, RemoteCallSemantics rcs, const std::string &subInterface, int fnId, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) const
        {
            bool oneway = (Oneway == rcs);
            ConnectionResetGuard connectionResetGuard(clientStub);
            RCF::IDL::DoRequest(clientStub, oneway, subInterface, fnId, clientStub.out);
            RCF::IDL::InParameter< A1 >(a1, clientStub.out);
            RCF::IDL::InParameter< A2 >(a2, clientStub.out);
            RCF::IDL::InParameter< A3 >(a3, clientStub.out);
            RCF::IDL::InParameter< A4 >(a4, clientStub.out);
            RCF::IDL::InParameter< A5 >(a5, clientStub.out);
            RCF::IDL::InReturnValue< R > ret(clientStub, clientStub.in, clientStub.out, oneway);
            RCF::IDL::OutParameter< A1 >(a1, clientStub.in, oneway);
            RCF::IDL::OutParameter< A2 >(a2, clientStub.in, oneway);
            RCF::IDL::OutParameter< A3 >(a3, clientStub.in, oneway);
            RCF::IDL::OutParameter< A4 >(a4, clientStub.in, oneway);
            RCF::IDL::OutParameter< A5 >(a5, clientStub.in, oneway);
            connectionResetGuard.dismiss();
            return ret.get();
        }
    };

} // namespace RCF

#include <RCF/Marshal.inl>

#endif // ! INCLUDE_RCF_MARSHAL_HPP

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Australia Australia
Software developer, from Sweden and now living in Canberra, Australia, working on distributed C++ applications. When he is not programming, Jarl enjoys skiing and playing table tennis. He derives immense satisfaction from referring to himself in third person.

Comments and Discussions