libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
info.hpp
Go to the documentation of this file.
1 #ifndef LIBJMMCG_CORE_INFO_HPP
2 #define LIBJMMCG_CORE_INFO_HPP
3 
4 /******************************************************************************
5 ** Copyright © 2002 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 
23 
24 #include <cassert>
25 #include <climits>
26 #include <functional>
27 #include <iterator>
28 #include <map>
29 #include <typeinfo>
30 #include <vector>
31 
32 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE {
33 
34  /// All of this stuff is for high-quality error reporting.
35  /**
36  You construct a file-scope revision object to contain and wrap the basic CVS, RCS, etc revision strings.
37  Then when you throw an exception, it will take this revision object and dump it when "what()" is called on the exception to give you that file information to aid debugging run-time bug requests from clients of your code.
38  You can add a function object, which takes argument objects to the exception to provide yet more information, like location in the file, function name, argument names and values, the type if the "this" object (handy in template expansions to know which template expansion has occurred if there are many different specialisations).
39 
40  \see revision
41  \see function
42  \see exception
43  */
44  namespace info {
45  /// Attempt to include revision information for the file from which the exception was constructed.
46  /**
47  This should be added at source-file scope, although it does support header-file usage too.
48  At source file-scope the macro JMMCG_REVISION_CONSTANT is provided to allow trivial use.
49  In a header file the macro JMMCG_REVISION_HDR is provided to ease use.
50 
51  \see JMMCG_REVISION_CONSTANT
52  \see JMMCG_REVISION_HDR
53  */
54  class revision {
55  public:
56  constexpr __stdcall revision(const tchar *r=_T(LIBJMMCG_VERSION_NUMBER), const char *f=__FILE__) noexcept(true)
57  : rev(r), file(f) {
58  }
59  __stdcall revision(const revision &r) noexcept(true)
60  : rev(r.rev), file(r.file) {
61  }
62 
63  static constexpr std::size_t max_size() noexcept(true) {
64  return 9+PATH_MAX+28+32+33+PATH_MAX+3;
65  }
66 
67  /**
68  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
69  */
70  friend inline tostream & __fastcall operator<<(tostream &o, const revision &ri) noexcept(false) {
71  o<<_T("Revision: '")<<ri.rev<<_T("'.\n")
72  _T("Local file name: '")<<StringToTString(ri.file)<<_T("'.\n");
73  return o;
74  }
75 
76  private:
77  const tchar *rev;
78  const char *file;
79  };
80 
81  /// Attempt to include function-call information for the function from which the exception was constructed.
82  /**
83  Use of the macros JMMCG_FUNCTION (for no arguments) & JMMCG_FUNCTION1 (for one argument) make use of this object for constructing an exception easier.
84 
85  \see JMMCG_FUNCTION
86  \see JMMCG_FUNCTION1
87  */
88  class function {
89  public:
90  /// Attempt to include argument information for the function from which the exception was constructed.
91  class argument {
92  public:
93 #if defined(_MSC_VER) && (_MSC_VER < 1300)
94  // Crappy M$ compiler can't handle member-template functions.
95  __stdcall argument(tstring &&n, tstring &&v) noexcept(false)
96  : name(std::move(n)), value(std::move(v)) {
97  assert(CheckVals());
98  }
99 #else
100  template<typename Arg_Val> __declspec(dllexport) argument(tstring &&n, const Arg_Val &v) noexcept(false)
101  : name(std::move(n)), value(tostring(v)) {
102  assert(CheckVals());
103  }
104 #endif // _MSC_VER
105 
106  static constexpr std::size_t max_size() noexcept(true) {
107  return 8+PATH_MAX+10+PATH_MAX+4;
108  }
109 
110  /**
111  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
112  */
113  friend inline tostream & __fastcall operator<<(tostream &o, const argument &a) noexcept(false) {
114  assert(a.CheckVals());
115  o<<_T("\tName: '")<<a.name<<_T("', value: '")<<a.value<<_T("'.");
116  return o;
117  }
118 
119 #ifdef DEBUG
120  bool __fastcall CheckVals(void) const noexcept(true) {
121  return !name.empty();
122  }
123 #endif
124 
125  private:
126  const tstring name;
127  const tstring value;
128  };
130  static inline constexpr std::size_t max_args=10;
131 
132  explicit NEVER_INLINE __stdcall function(const unsigned long l=__LINE__, const char *n=NULL, const std::type_info &th=typeid(void), const argument &a=argument(_T("void"),_T("nothing"))) noexcept(false)
133  : line(l), name(n ? n : __PRETTY_FUNCTION__), this_type(th), args_details(1, argument(a)) {
134  assert(CheckVals());
135  }
136 
137  void NEVER_INLINE __fastcall add_arg(argument &&a) noexcept(false) {
138  assert(CheckVals());
139  args_details.emplace_back(std::move(a));
140  assert(CheckVals());
141  }
142 
143  template<class ...Args>
144  void NEVER_INLINE __fastcall add_args(Args &&...a) noexcept(false) {
145  assert(CheckVals());
146  args_details.reserve(args_details.size()+sizeof...(a));
147  (
148  args_details.emplace_back(std::move(a)), ...
149  );
150  assert(CheckVals());
151  }
152 
153  template<typename Arg_Val> void NEVER_INLINE __fastcall add_arg(tstring &&n, const Arg_Val &v) noexcept(false) {
154  assert(CheckVals());
155 #if defined(_MSC_VER) && (_MSC_VER < 1300)
156  // Crappy M$ compiler can't handle member-template functions.
157  add_arg(argument(std::move(n),tostring(v)));
158 #else
159  add_arg(argument(std::move(n),v));
160 #endif // _MSC_VER
161  assert(CheckVals());
162  }
163 
164  static constexpr std::size_t max_size() noexcept(true) {
165  return 15+PATH_MAX+3
166  +19+PATH_MAX+3
167  +39+std::numeric_limits<std::size_t>::digits10+1
169  +13+std::numeric_limits<std::size_t>::digits10+3;
170  }
171 
172  /**
173  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
174  */
175  friend inline tostream & __fastcall operator<<(tostream &o, const function &fi) noexcept(false) {
176  o<<_T("Function name: '")<<StringToTString(fi.name)<<_T("'.\n")
177  _T("C++ RTTI context: '")<<fi.this_type.name()<<_T("'.\n")
178  _T("Argument details, number of arguments: ")<<fi.args_details.size()<<_T("\n");
179  assert(fi.CheckVals());
180  std::copy(
181  fi.args_details.begin(),
182  fi.args_details.end(),
183  std::ostream_iterator<args_details_type::value_type,tchar>(o,_T("\n"))
184  );
185  o<<_T("Line number: ")<<std::dec<<fi.line<<_T(".\n");
186  return o;
187  }
188 
189  private:
190  const unsigned long line;
191  const char *name;
192  const std::type_info &this_type;
193  args_details_type args_details;
194 
195 #ifdef DEBUG
196  bool __fastcall CheckVals(void) const noexcept(true) {
198  for (auto const &i : args_details) {
199  const bool trouble=i.CheckVals();
200  if (!trouble) {
201  return false;
202  }
203  }
204  return true;
205  }
206 #endif
207  };
208  }
209 
210 } }
211 
212 /// Much as I despise macros, I despise adding error information more. (That's why I wrote these classes first - just to get it out of the way, so I can get on with more fun stuff...
213 /**
214  So these take some of the tedium out of adding that boiler-plate error-information that may come in handy that once-in-a-blue-moon occasion I didn't bother to put it in, because one finds it just too tedious....)
215 
216  \see revision
217 */
218 
219 /// Handy for use in header files for constructing "libjmmcg::exception"s, but see that file for a more useful macro.
220 #define JMMCG_REVISION_HDR(_JMMCG_REVISION_HDR) jmmcg::LIBJMMCG_VER_NAMESPACE::info::revision((_JMMCG_REVISION_HDR),__FILE__)
221 
222 /// Introduces the constant "__REV_INFO__" (c.f. __LINE__, etc) into a private namespace for that compilation unit.
223 /**
224  An easier way of getting this boiler-plate revision information into your source files.
225  Handy for source files.
226 
227  \see revision
228 */
229 #define JMMCG_REVISION_CONSTANT(_JMMCG_REVISION_HDR)
230  namespace {
231  [[maybe_unused]] const jmmcg::LIBJMMCG_VER_NAMESPACE::info::revision __REV_INFO__((_JMMCG_REVISION_HDR),__FILE__);
232  }
233 
234 /// Construction a function error-information object with no arguments.
235 /**
236  Eases construction of this object in a source or header file.
237 
238  \see function
239 */
240 # define JMMCG_FUNCTION(JMMCG_FUNCTION_TYPE_) jmmcg::LIBJMMCG_VER_NAMESPACE::info::function(__LINE__,__PRETTY_FUNCTION__,typeid(JMMCG_FUNCTION_TYPE_))
241 
242 /// Construction a function error-information object with one argument.
243 /**
244  Eases construction of this object in a source or header file.
245 
246  \see function
247 */
248 # define JMMCG_FUNCTION1(JMMCG_FUNCTION_TYPE_,JMMCG_FUNCTION_ARGUMENTS_) jmmcg::LIBJMMCG_VER_NAMESPACE::info::function(__LINE__,__PRETTY_FUNCTION__,typeid(JMMCG_FUNCTION_TYPE_),(JMMCG_FUNCTION_ARGUMENTS_))
249 
250 #endif