rpc.hh 5.4 KB

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