Home | History | Annotate | Line # | Download | only in libcc1
rpc.hh revision 1.7
      1 /* RPC call and callback templates
      2    Copyright (C) 2014-2022 Free Software Foundation, Inc.
      3 
      4 This file is part of GCC.
      5 
      6 GCC is free software; you can redistribute it and/or modify it under
      7 the terms of the GNU General Public License as published by the Free
      8 Software Foundation; either version 3, or (at your option) any later
      9 version.
     10 
     11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14 for more details.
     15 
     16 You should have received a copy of the GNU General Public License
     17 along with GCC; see the file COPYING3.  If not see
     18 <http://www.gnu.org/licenses/>.  */
     19 
     20 #ifndef CC1_PLUGIN_RPC_HH
     21 #define CC1_PLUGIN_RPC_HH
     22 
     23 #include "status.hh"
     24 #include "connection.hh"
     25 #include "deleter.hh"
     26 
     27 namespace cc1_plugin
     28 {
     29   // The plugin API may contain some "const" method parameters.
     30   // However, when unmarshalling we cannot unmarshall into a const
     31   // object; and furthermore we want to be able to deallocate pointers
     32   // when finished with them.  This wrapper class lets us properly
     33   // remove the "const" and handle deallocation from pointer types.
     34 
     35   template<typename T>
     36   class argument_wrapper
     37   {
     38   public:
     39 
     40     argument_wrapper () { }
     41     ~argument_wrapper () { }
     42 
     43     argument_wrapper (const argument_wrapper &) = delete;
     44     argument_wrapper &operator= (const argument_wrapper &) = delete;
     45 
     46     T get () const { return m_object; }
     47 
     48     status unmarshall (connection *conn)
     49     {
     50       return ::cc1_plugin::unmarshall (conn, &m_object);
     51     }
     52 
     53   private:
     54 
     55     T m_object;
     56   };
     57 
     58   // Specialization for any kind of pointer.
     59   template<typename T>
     60   class argument_wrapper<T *>
     61   {
     62   public:
     63     argument_wrapper () = default;
     64     ~argument_wrapper () = default;
     65 
     66     argument_wrapper (const argument_wrapper &) = delete;
     67     argument_wrapper &operator= (const argument_wrapper &) = delete;
     68 
     69     typedef typename std::remove_const<T>::type type;
     70 
     71     const type *get () const
     72     {
     73       return m_object.get ();
     74     }
     75 
     76     status unmarshall (connection *conn)
     77     {
     78       type *ptr;
     79       if (!::cc1_plugin::unmarshall (conn, &ptr))
     80 	return FAIL;
     81       m_object.reset (ptr);
     82       return OK;
     83     }
     84 
     85   private:
     86 
     87     unique_ptr<type> m_object;
     88   };
     89 
     90   // There are two kinds of template functions here: "call" and
     91   // "invoker".
     92 
     93   // The "call" template is used for making a remote procedure call.
     94   // It starts a query ('Q') packet, marshalls its arguments, waits
     95   // for a result, and finally reads and returns the result via an
     96   // "out" parameter.
     97 
     98   // The "invoker" template is used when receiving a remote procedure
     99   // call.  This template function is suitable for use with the
    100   // "callbacks" and "connection" classes.  It decodes incoming
    101   // arguments, passes them to the wrapped function, and finally
    102   // marshalls a reply packet.
    103 
    104   template<typename R, typename... Arg>
    105   status
    106   call (connection *conn, const char *method, R *result, Arg... args)
    107   {
    108     if (!conn->send ('Q'))
    109       return FAIL;
    110     if (!marshall (conn, method))
    111       return FAIL;
    112     if (!marshall (conn, (int) sizeof... (Arg)))
    113       return FAIL;
    114     if (!marshall (conn, args...))
    115       return FAIL;
    116     if (!conn->wait_for_result ())
    117       return FAIL;
    118     if (!unmarshall (conn, result))
    119       return FAIL;
    120     return OK;
    121   }
    122 
    123   // The base case -- just return OK.
    124   template<int I, typename... T>
    125   typename std::enable_if<I == sizeof... (T), status>::type
    126   unmarshall (connection *, std::tuple<T...> &)
    127   {
    128     return OK;
    129   }
    130 
    131   // Unmarshall this argument, then unmarshall all subsequent args.
    132   template<int I, typename... T>
    133   typename std::enable_if<I < sizeof... (T), status>::type
    134   unmarshall (connection *conn, std::tuple<T...> &value)
    135   {
    136     if (!std::get<I> (value).unmarshall (conn))
    137       return FAIL;
    138     return unmarshall<I + 1, T...> (conn, value);
    139   }
    140 
    141   // Wrap a static function that is suitable for use as a callback.
    142   // This is a template function inside a template class to work
    143   // around limitations with multiple variadic packs.
    144   template<typename R, typename... Arg>
    145   class invoker
    146   {
    147     // Base case -- we can call the function.
    148     template<int I, R func (connection *, Arg...), typename... T>
    149     static typename std::enable_if<I == sizeof... (Arg), R>::type
    150     call (connection *conn, const std::tuple<argument_wrapper<Arg>...> &,
    151 	  T... args)
    152     {
    153       return func (conn, args...);
    154     }
    155 
    156     // Unpack one argument and continue the recursion.
    157     template<int I, R func (connection *, Arg...), typename... T>
    158     static typename std::enable_if<I < sizeof... (Arg), R>::type
    159     call (connection *conn, const std::tuple<argument_wrapper<Arg>...> &value,
    160 	  T... args)
    161     {
    162       return call<I + 1, func> (conn, value, args...,
    163 				std::get<I> (value).get ());
    164     }
    165 
    166   public:
    167 
    168     // A callback function that reads arguments from the connection,
    169     // calls the wrapped function, and then sends the result back on
    170     // the connection.
    171     template<R func (connection *, Arg...)>
    172     static status
    173     invoke (connection *conn)
    174     {
    175       if (!unmarshall_check (conn, sizeof... (Arg)))
    176 	return FAIL;
    177       std::tuple<argument_wrapper<Arg>...> wrapped;
    178       if (!unmarshall<0> (conn, wrapped))
    179 	return FAIL;
    180 
    181       R result = call<0, func> (conn, wrapped);
    182 
    183       if (!conn->send ('R'))
    184 	return FAIL;
    185       return marshall (conn, result);
    186     }
    187   };
    188 };
    189 
    190 #endif // CC1_PLUGIN_RPC_HH
    191