libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
syscall_wrapper.hpp
Go to the documentation of this file.
1 #ifndef LIBJMMCG_CORE_SYSCALL_WRAPPER_HPP
2 #define LIBJMMCG_CORE_SYSCALL_WRAPPER_HPP
3 
4 /******************************************************************************
5 ** Copyright © 2020 by J.M.McGuiness, coder@hussar.me.uk
6 **
7 ** This library is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU Lesser General Public
9 ** License as published by the Free Software Foundation; either
10 ** version 2.1 of the License, or (at your option) any later version.
11 **
12 ** This library is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ** Lesser General Public License for more details.
16 **
17 ** You should have received a copy of the GNU Lesser General Public
18 ** License along with this library; if not, write to the Free Software
19 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 
22 #include "exception.hpp"
23 
24 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE {
25 
26 /// A wrapper for glibc or syscalls, etc. This function captures the return-code and if it is the specified failure-code, then according to the traits may capture any error information and throw it as an exception.
27 namespace syscall {
28 
29  using exception_t=crt_exception<ppd::platform_api, ppd::heavyweight_threading>;
30 
31  namespace private_ {
32 
33  template<class ReturnType>
34  struct failure_code;
35 
36  template<>
37  struct failure_code<ssize_t> {
38  static inline constexpr ssize_t value=0;
39  };
40  template<>
41  struct failure_code<int> {
42  static inline constexpr int value=-1;
43  };
44  template<>
45  struct failure_code<void*> {
46  static inline constexpr void *value=nullptr;
47  };
48  template<>
49  struct failure_code<sighandler_t> {
50  static inline const sighandler_t value=reinterpret_cast<sighandler_t>(-1);
51  };
52  template<>
53  struct failure_code<void> {
54  };
55 
56  template<class ReturnType>
57  struct failure_detection;
58 
59  template<>
60  struct failure_detection<ssize_t> {
61  static constexpr bool result(ssize_t ret_code, ssize_t fv) noexcept(true) {
62  return ret_code<fv;
63  }
64  };
65  template<>
66  struct failure_detection<int> {
67  static constexpr bool result(int ret_code, int fv) noexcept(true) {
68  return ret_code<=fv;
69  }
70  };
71  template<>
72  struct failure_detection<void *> {
73  static constexpr bool result(void *ret_code, void *fv) noexcept(true) {
74  return ret_code==fv;
75  }
76  };
77  template<>
78  struct failure_detection<sighandler_t> {
79  static constexpr bool result(sighandler_t ret_code, sighandler_t fv) noexcept(true) {
80  return ret_code==fv;
81  }
82  };
83 
84  template<>
85  struct failure_detection<void> {
86  static constexpr void result() noexcept(true) {
87  }
88  };
89 
90  template<std::size_t N, class CaseStatements>
91  struct unroller {
92  using type=typename std::tuple_element<N, CaseStatements>::type;
93 
94  template<class ReportError>
95  static auto result(int v, ReportError &&report_error) noexcept(false) {
96  if (v==type::label) {
97  return type::statement();
98  } else {
99  return unroller<N-1, CaseStatements>::result(v, std::move(report_error));
100  }
101  }
102  };
103  template<class CaseStatements>
104  struct unroller<0, CaseStatements> {
105  using type=typename std::tuple_element<0, CaseStatements>::type;
106 
107  template<class ReportError>
108  static auto result(int v, ReportError &&report_error) noexcept(false /*noexcept(type::statement()) && noexcept(report_error())*/) {
109  if (v==type::label) {
110  return type::statement();
111  } else {
112  report_error();
113  return decltype(type::statement())();
114  }
115  }
116  };
117 
118  }
119 
120  template<
121  int RetCode,
122  auto Stmnt
123  >
125  enum : int {
126  label=RetCode
127  };
128  static inline constexpr auto statement{Stmnt};
129  };
130 
131  template<class ...CaseLabels>
133  using case_labels=std::tuple<CaseLabels...>;
134  };
135 
136  template<
137  class ReturnType,
138  template<class> class FailureCode,
139  template<class> class FailureDetection
140  > struct failure_traits {
141  using fn_ret_code=ReturnType;
142  using failure_code=FailureCode<fn_ret_code>;
143  using failure_detection=FailureDetection<fn_ret_code>;
144  };
145 
146  /// The base of any trait in this file. This trait calls handle_error() if it detects a failure, otherwise returns the result of the wrapped function.
147  template<
148  class FailureTraits,
149  class ...Args
150  >
151  struct traits_base {
152  using exception_t=syscall::exception_t;
153  using failure_traits=FailureTraits;
154  using fn_ret_code=typename failure_traits::fn_ret_code;
155  using failure_detection=typename failure_traits::failure_detection;
156  using failure_code=typename failure_traits::failure_code;
157 
158  /// This operation is run to either return the result of the wrapped function or execute handle_error(), which should throw an exception. (Otherwise what is the point of this wrapper?)
159  template<class HandleError>
160  static auto report(fn_ret_code ret, HandleError &&handle_error) noexcept(false);
161 
162  /// How the error should be reported to the client, in this case a crt_exception with suitable details is thrown.
163  static void report_error(int err, char const *file_name, unsigned line_num, char const *fn_name, tchar const * const rev_info, tchar const * const err_msg, fn_ret_code (*fn)(Args...), Args ...args) noexcept(false) NEVER_INLINE;
164  };
165 
166  /// This is the default trait.
167  template<
168  class FailureTraits,
169  class ...Args
170  >
171  struct simple_report_traits : public traits_base<FailureTraits, Args...> {
172  using base_t=traits_base<FailureTraits, Args...>;
173  using typename base_t::fn_ret_code;
174  using typename base_t::failure_detection;
175  using typename base_t::failure_code;
176 
177  /// The way an error is identified and operated upon.
178  static auto select_error(int err, char const *file_name, unsigned line_num, char const *fn_name, tchar const * const rev_info, tchar const * const err_msg, fn_ret_code (*fn)(Args...), Args ...args);
179  };
180 
181  /// If different operations need to be performed on different return-codes, then this trait may be used.
182  template<
183  class CaseStatements, ///< A collection of exit-code & operation pairs.
184  class FailureTraits,
185  class ...Args
186  >
187  struct switch_traits : public traits_base<FailureTraits, Args...> {
188  using base_t=traits_base<FailureTraits, Args...>;
189  using typename base_t::fn_ret_code;
190  using typename base_t::failure_detection;
191  using typename base_t::failure_code;
192 
193  /**
194  Create this type like this example:
195 
196  using all_case_statements=syscall::case_statements_t<
197  syscall::a_case_statement<1, [](){return 1;}>,
198  syscall::a_case_statement<2, [](){return 2;}>
199  >;
200 
201  \see case_statements_t, a_case_statement
202  */
203  using case_statements=typename CaseStatements::case_labels;
204 
205  static constexpr auto select_error(int err, char const *file_name, unsigned line_num, char const *fn_name, tchar const * const rev_info, tchar const * const err_msg, fn_ret_code (*fn)(Args...), Args ...args);
206  };
207 
208  template<
209  class CaseStatements
210  >
212  template<
213  class FailureTraits,
214  class ...Args
215  >
216  using type=switch_traits<CaseStatements, FailureTraits, Args...>;
217  };
218 
219  /// The function that wraps calling the C-style function.
220  template<
221  template<class, class ...> class Traits,
222  template<class, template<class> class, template<class> class> class FailureTraits,
223  template<class> class FailureCode,
224  template<class> class FailureDetection,
225  class FnReturnType, ///< The return-type deduced from the passed in C-style function.
226  class ...FnArgs, ///< The types of the arguments deduced from the passed in C-style function.
227  class ...PassedInArgs ///< In C++ one may commonly use slightly different parameter-types, e.g. an enum for a set of integer-values or a bitmask, so we need to accept these, as implicit conversions are not permitted in template-matching for function overloads. Minor conversions are permitted, using static_cast<...>(...).
228  >
229  FnReturnType
230  process(char const * const file_name, unsigned line_num, char const * const fn_name, tchar const * const rev_info, tchar const * const err_msg, FnReturnType (*fn)(FnArgs...), PassedInArgs ...args) noexcept(std::is_same<FnReturnType, void>::value);
231 }
232 
233 } }
234 
235 /// Use this nasty macro to wrap calling the C-style function for which the return code is basically a "single" failure-code and a range of successful ones.
236 /**
237  \todo This define could be removed in C++20 with the use of std::source_location, does not work with g++ v9.2.
238 */
239 #define JMMCG_SYSCALL_WRAPPER(ErrMsg, RevInfo, Fn, ...)
240  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::process<
241  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::simple_report_traits,
242  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::failure_traits,
243  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::private_::failure_code,
244  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::private_::failure_detection
245  >(__FILE__, __LINE__, LIBJMMCG_ENQUOTE(Fn), _T(LIBJMMCG_VERSION_NUMBER), ErrMsg, Fn, __VA_ARGS__)
246 
247 #define JMMCG_SYSCALL_WRAPPER_FC(FailureCode, ErrMsg, RevInfo, Fn, ...)
248  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::process<
249  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::simple_report_traits,
250  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::failure_traits,
251  FailureCode,
252  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::private_::failure_detection
253  >(__FILE__, __LINE__, LIBJMMCG_ENQUOTE(Fn), RevInfo, ErrMsg, Fn, __VA_ARGS__)
254 
255 /// Use this nasty macro to wrap calling the C-style function for which there may be many failure codes that need different handling for each.
256 /**
257  \todo This define could be removed in C++20 with the use of std::source_location, does not work with g++ v9.2.
258 */
259 #define JMMCG_SYSCALL_WRAPPER_SWITCH(CaseLabels, ErrMsg, RevInfo, Fn, ...)
260  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::process<
261  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::switch_wrapper_t<
262  CaseLabels
263  >::type,
264  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::failure_traits,
265  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::private_::failure_code,
266  jmmcg::LIBJMMCG_VER_NAMESPACE::syscall::private_::failure_detection
267  >(__FILE__, __LINE__, LIBJMMCG_ENQUOTE(Fn), RevInfo, ErrMsg, Fn, __VA_ARGS__)
268 
270 
271 #endif