CodeSonar C++ API
[For improved navigation, enable JavaScript.]
cs_point_set_decl.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023, an unpublished work by CodeSecure, Inc.
3  * ALL RIGHTS RESERVED
4  *
5  * Copyright (c) 2013-2023, an unpublished work by GrammaTech, Inc.
6  * ALL RIGHTS RESERVED
7  *
8  * This software is furnished under a license and may be used and
9  * copied only in accordance with the terms of such license and the
10  * inclusion of the above copyright notice. This software or any
11  * other copies thereof may not be provided or otherwise made
12  * available to any other person. Title to and ownership of the
13  * software is retained by CodeSecure, Inc.
14  */
15 
16 #ifndef CS_POINT_SET_DECL_HPP
17 #define CS_POINT_SET_DECL_HPP
18 
19 #include "cs_set.hpp"
20 #include "cs_procedure_fwd.hpp"
21 #include "cs_point_fwd.hpp"
22 #include "cs_pdg_edge_kind.hpp"
23 #include "cs_pdg_vertex_set.h"
24 #include "cs_scratchpad.hpp"
25 #include "cs_depfilter.hpp"
26 #include "cs_symbol_set_decl.hpp"
27 
28 #define CS_SET_IMPL_ID(name) cs_pdg_vertex_ ## name
29 #define CS_SET_IMPL_KEY point
30 #define CS_SET_IMPL_MUTABLE 2
31 #define CS_SET_IMPL_HAS_MULTI_UNION
32 #include "cs_set_impl.hpp"
33 
34 
35 
38 namespace cs{
39  template<>
40  class set_mixin<point_set>;
41  template<>
42  class cglue<point_set>
43  {
44  cglue();
45  friend class set_mixin<mutable_set<symbol> >;
46  friend class set_mixin<mutable_set<int_pair> >;
47  friend class point;
48  friend class procedure;
49  friend class symbol;
50  friend class cglue<compunit_points>;
51  friend class cglue<compunit_points_pair>;
52  friend class cglue<exceptional_return>;
53  friend class cglue<interest_set>;
54  friend class int_pairs_to_points_map;
55  friend class project;
56  friend class set_mixin<point_set>;
57  public:
58  typedef cs_pdg_vertex_set ctype;
59  typedef point_set type;
60  CS_CXX_API_CGLUE_ACCESS_MODIFIER:
61  static type wrap(const ctype &c, bool frozen);
62  static ctype unwrap(const type &c);
63  };
64 
65  template<>
66  class cglue<compunit_points>
67  {
68  cglue();
69  friend class scratchpad<compunit_points>;
70  public:
71  typedef compunit_points type;
72  typedef cs_uid_pdgvslist ctype;
73  CS_CXX_API_CGLUE_ACCESS_MODIFIER:
74  static compunit_points wrap(const ctype &c);
75  };
76 
77  template<>
78  class cglue<compunit_points_pair>
79  {
80  cglue();
81  friend class scratchpad<compunit_points_pair>;
82  public:
83  typedef compunit_points_pair type;
84  typedef cs_uid_pdgvs_pair ctype;
85  CS_CXX_API_CGLUE_ACCESS_MODIFIER:
86  static compunit_points_pair wrap(const ctype &c);
87 
88  /* unwrap intentionally omitted due to memory ownership
89  * confusion.
90  */
91  };
92 
102  class predsucc_filter_directive{
103  CS_ENUM_BOILERPLATE_NONAME(predsucc_filter_directive,
104  cs_ps_enum,
105  (csuint64)_inner < (csuint64)csps_count)
106  public:
111  std::string name() const
112  {
113  const char *rv;
114  check(cs_ps_enum_name(
115  inner, &rv));
116  return rv;
117  }
118 
120  predsucc_filter_directive()
121  : inner(csps_stop){}
123  };
124  CS_ENUM_BOILERPLATE_FRIENDS(predsucc_filter_directive,
125  friend class cglue<predsucc_filter_pair>;,
126  cs
127  )
128 #include "cs_predsucc_filter_directive_defs.hpp"
132  template<>
133  class cglue<predsucc_filter_pair>
134  {
135  friend class set_mixin<point_set>;
136  cglue();
137  public:
138  typedef predsucc_filter_pair type;
139  typedef cs_ps_return_type ctype;
140  CS_CXX_API_CGLUE_ACCESS_MODIFIER:
141  static type wrap(const ctype &c)
142  {
143  return type(cglue<predsucc_filter_directive>::wrap(c.mode),
144  cglue<pdg_edge_kind>::wrap(c.edge));
145  }
146  static ctype unwrap(const type &c)
147  {
148  ctype rv;
149  rv.mode = cglue<predsucc_filter_directive>::unwrap(c.first);
150  rv.edge = cglue<pdg_edge_kind>::unwrap(c.second);
151  return rv;
152  }
153  };
156 #ifdef SWIG
157 #if defined(SWIGPYTHON)
158  %rename(_predsucc_filter) predsucc_filter;
159 #endif
160 #if defined(SWIGCSHARP)
161  %rename(_predsucc_filter_csharp) predsucc_filter;
162  %typemap(csclassmodifiers) predsucc_filter "public abstract class";
163 #endif
164 #else
165 
166  struct filtered_predsucc_swig_exc{};
168 /* The following is important to avoid multiply defined symbols, which
169  * swig will otherwise cause. it is hackish, but effective.
170  */
171 #if defined(SWIGCSHARP)
172 #define SwigDirector_predsucc_filter CSharp_SwigDirector_predsucc_filter
173 #elif defined(SWIGJAVA)
174 #define SwigDirector_predsucc_filter Java_SwigDirector_predsucc_filter
175 #elif defined(SWIGOCAML)
176 #define SwigDirector_predsucc_filter OCaml_SwigDirector_predsucc_filter
177 #endif
178 #endif
179 
183  class predsucc_filter
184  {
185  public:
186 #if defined(SWIG) && defined(SWIGJAVA)
187  %rename(visit) operator();
188 #endif
189 #if defined(SWIG) && defined(SWIGOCAML)
190  %rename(visit) operator();
191 #endif
192 #if defined(SWIG) && defined(SWIGCSHARP)
193  %rename(_visit) operator();
194 #endif
195 #if defined(SWIG) && defined(SWIGJAVA)
196  %javaexception("java.lang.Exception") operator() {
197  $action
198  }
199 #endif
200 
214  virtual predsucc_filter_pair operator()(point p) = 0;
215  virtual ~predsucc_filter(){}
216  };
218 #if defined(SWIG) && defined(SWIGPYTHON)
219  %pythoncode{
220  class _predsucc_filter2(_predsucc_filter):
221  def __init__(self, func):
222  _predsucc_filter.__init__(self)
223  self.func = func
224  def __call__(self, pnt): return self.func(pnt)
225  }
226 #endif
227  /* csharp predsucc_filter is in handwritten predsucc_filter. */
228 
235  template<> class set_mixin<mutable_set<point> >
236  {
237  cs_pdg_vertex_set unwrap() const
238  { return cglue<point_set>::unwrap(*static_cast<const point_set*>(this)); }
239  protected:
240  set_mixin(){}
241  public:
242 
253  int_pair_set to_int_pair_set_in_sfileinst(sfileinst u) const;
254 
255 
263  size_t intersect_size(const point_set &other) const
264  {
265  size_t rv;
266  check(cs_pdg_vertex_set_intersect_size(
267  unwrap(), cglue<point_set>::unwrap(other),
268  &rv));
269  return rv;
270  }
271 
292  bool cycle(pdg_edge_kind k) const
293  {
294  cs_boolean rv;
295  check(cs_pdg_vertex_set_cycle(unwrap(),
296  cglue<pdg_edge_kind>::unwrap(k), &rv));
297  return !!rv;
298  }
312  compunit_points_vector categorize() const;
313 
314 
325  compunit_points_pair_vector categorize_by_file() const;
326 
327  /* multi_union implemented as constructor */
328 
329 
342  bool intersects_procedure(procedure p) const;
343 
344 
356  std::vector<procedure> procedures() const;
357 
358 
371  std::vector<point> sort() const;
372 
373 
393  std::vector<point> sort(const std::string &file_name) const;
394 
395 
428  point_set predecessors(const depfilter &f, size_t threshold=0) const
429  {
430  cs_pdg_vertex_set rv;
431  check(cs_s_predecessors(
432  unwrap(),
433  cglue<depfilter>::unwrap_ptr(f),
434  threshold,
435  &rv));
436  return cglue<point_set>::wrap(rv, false);
437  }
473  point_set successors(const depfilter &f, size_t threshold=0) const
474  {
475  cs_pdg_vertex_set rv;
476  check(cs_s_successors(
477  unwrap(),
478  cglue<depfilter>::unwrap_ptr(f),
479  threshold,
480  &rv));
481  return cglue<point_set>::wrap(rv, false);
482  }
513  point_set predecessors(size_t threshold=0) const
514  {
515  cs_pdg_vertex_set rv;
516  depfilter f;
517  check(cs_s_predecessors(
518  unwrap(),
519  cglue<depfilter>::unwrap_ptr(f),
520  threshold,
521  &rv));
522  return cglue<point_set>::wrap(rv, false);
523  }
554  point_set successors(size_t threshold=0) const
555  {
556  cs_pdg_vertex_set rv;
557  depfilter f;
558  check(cs_s_successors(
559  unwrap(),
560  cglue<depfilter>::unwrap_ptr(f),
561  threshold,
562  &rv));
563  return cglue<point_set>::wrap(rv, false);
564  }
596  point_set backward_slice(pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
597  {
598  cs_pdg_vertex_set rv;
599  check(cs_s_slice(unwrap(), cglue<pdg_edge_kind>::unwrap(k), &rv));
600  return cglue<point_set>::wrap(rv, false);
601  }
633  point_set forward_slice(pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
634  {
635  cs_pdg_vertex_set rv;
636  check(cs_s_forward_slice(unwrap(), cglue<pdg_edge_kind>::unwrap(k), &rv));
637  return cglue<point_set>::wrap(rv, false);
638  }
674  point_set backward_slice_out(pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
675  {
676  cs_pdg_vertex_set rv;
677  check(cs_s_slice_out(unwrap(), cglue<pdg_edge_kind>::unwrap(k), &rv));
678  return cglue<point_set>::wrap(rv, false);
679  }
715  point_set forward_slice_out(pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
716  {
717  cs_pdg_vertex_set rv;
718  check(cs_s_forward_slice_out(unwrap(), cglue<pdg_edge_kind>::unwrap(k), &rv));
719  return cglue<point_set>::wrap(rv, false);
720  }
756  point_set backward_slice_in(pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
757  {
758  cs_pdg_vertex_set rv;
759  check(cs_s_slice_in(unwrap(), cglue<pdg_edge_kind>::unwrap(k), &rv));
760  return cglue<point_set>::wrap(rv, false);
761  }
797  point_set forward_slice_in(pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
798  {
799  cs_pdg_vertex_set rv;
800  check(cs_s_forward_slice_in(unwrap(), cglue<pdg_edge_kind>::unwrap(k), &rv));
801  return cglue<point_set>::wrap(rv, false);
802  }
835  point_set chop(const point_set &targets,
836  pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
837  {
838  cs_pdg_vertex_set rv;
839  check(cs_s_chop(unwrap(), cglue<point_set>::unwrap(targets), cglue<pdg_edge_kind>::unwrap(k), &rv));
840  return cglue<point_set>::wrap(rv, false);
841  }
878  point_set truncated_chop(
879  const point_set &targets,
880  pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
881  {
882  cs_pdg_vertex_set rv;
883  check(cs_s_truncated_chop(unwrap(), cglue<point_set>::unwrap(targets), cglue<pdg_edge_kind>::unwrap(k), &rv));
884  return cglue<point_set>::wrap(rv, false);
885  }
917  point_set fast_chop(
918  const point_set &targets,
919  pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
920  {
921  cs_pdg_vertex_set rv;
922  check(cs_s_fast_chop(unwrap(), cglue<point_set>::unwrap(targets), cglue<pdg_edge_kind>::unwrap(k), &rv));
923  return cglue<point_set>::wrap(rv, false);
924  }
972  point_set var_predecessors(
973  const symbol_set &variables,
974  const depfilter &f,
975  size_t threshold = 0) const
976  {
977  cs_pdg_vertex_set rv;
978  check(cs_s_var_predecessors(unwrap(),
979  cglue<symbol_set>::unwrap(variables),
980  cglue<depfilter>::unwrap_ptr(f),
981  threshold, &rv));
982  return cglue<point_set>::wrap(rv, false);
983  }
1030  point_set var_successors(
1031  const symbol_set &variables,
1032  const depfilter &f,
1033  size_t threshold = 0) const
1034  {
1035  cs_pdg_vertex_set rv;
1036  check(cs_s_var_successors(unwrap(),
1037  cglue<symbol_set>::unwrap(variables),
1038  cglue<depfilter>::unwrap_ptr(f),
1039  threshold, &rv));
1040  return cglue<point_set>::wrap(rv, false);
1041  }
1078  point_set var_backward_slice(
1079  const symbol_set &variables,
1080  pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
1081  {
1082  cs_pdg_vertex_set rv;
1083  check(cs_s_var_slice(
1084  unwrap(),
1085  cglue<symbol_set>::unwrap(variables),
1086  cglue<pdg_edge_kind>::unwrap(k),
1087  &rv));
1088  return cglue<point_set>::wrap(rv, false);
1089  }
1126  point_set var_forward_slice(
1127  const symbol_set &variables,
1128  pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
1129  {
1130  cs_pdg_vertex_set rv;
1131  check(cs_s_var_forward_slice(
1132  unwrap(),
1133  cglue<symbol_set>::unwrap(variables),
1134  cglue<pdg_edge_kind>::unwrap(k),
1135  &rv));
1136  return cglue<point_set>::wrap(rv, false);
1137  }
1186  point_set var_chop(const symbol_set &variables,
1187  const point_set &targets,
1188  const symbol_set &target_variables,
1189  pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
1190  {
1191  cs_pdg_vertex_set rv;
1192  check(cs_s_var_chop(
1193  unwrap(),
1194  cglue<symbol_set>::unwrap(variables),
1195  cglue<point_set>::unwrap(targets),
1196  cglue<symbol_set>::unwrap(target_variables),
1197  cglue<pdg_edge_kind>::unwrap(k),
1198  &rv));
1199  return cglue<point_set>::wrap(rv, false);
1200  }
1249  point_set var_truncated_chop(const symbol_set &variables,
1250  const point_set &targets,
1251  const symbol_set &target_variables,
1252  pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
1253  {
1254  cs_pdg_vertex_set rv;
1255  check(cs_s_var_truncated_chop(
1256  unwrap(),
1257  cglue<symbol_set>::unwrap(variables),
1258  cglue<point_set>::unwrap(targets),
1259  cglue<symbol_set>::unwrap(target_variables),
1260  cglue<pdg_edge_kind>::unwrap(k),
1261  &rv));
1262  return cglue<point_set>::wrap(rv, false);
1263  }
1313  point_set var_fast_chop(const symbol_set &variables,
1314  const point_set &targets,
1315  const symbol_set &target_variables,
1316  pdg_edge_kind k = pdg_edge_kind::DATACONTROL) const
1317  {
1318  cs_pdg_vertex_set rv;
1319  check(cs_s_var_fast_chop(
1320  unwrap(),
1321  cglue<symbol_set>::unwrap(variables),
1322  cglue<point_set>::unwrap(targets),
1323  cglue<symbol_set>::unwrap(target_variables),
1324  cglue<pdg_edge_kind>::unwrap(k),
1325  &rv));
1326  return cglue<point_set>::wrap(rv, false);
1327  }
1330  private:
1331 #ifdef SWIG_BUILD
1332 /* Make sure the linker doesn't have the opportunity to choose the
1333  * wrong implementation...
1334  */
1335 #if defined(SWIGPYTHON)
1336 #define delegate_to_filter delegate_to_filter_python
1337 #define filtered_successors filtered_successors_python
1338 #define filtered_predecessors filtered_predecessors_python
1339 #elif defined(SWIGJAVA)
1340 #ifdef SWIG
1341 %rename(filtered_successors) filtered_successors_java;
1342 %rename(filtered_predecessors) filtered_predecessors_java;
1343 #endif
1344 #define delegate_to_filter delegate_to_filter_java
1345 #define filtered_successors filtered_successors_java
1346 #define filtered_predecessors filtered_predecessors_java
1347 #elif defined(SWIGCSHARP)
1348 #ifdef SWIG
1349 %rename(_filtered_successors) filtered_successors_csharp;
1350 %rename(_filtered_predecessors) filtered_predecessors_csharp;
1351 #endif
1352 #define delegate_to_filter delegate_to_filter_csharp
1353 #define filtered_successors filtered_successors_csharp
1354 #define filtered_predecessors filtered_predecessors_csharp
1355 #elif defined(SWIGOCAML)
1356 #ifdef SWIG
1357 %rename(filtered_successors) filtered_successors_ocaml;
1358 %rename(filtered_predecessors) filtered_predecessors_ocaml;
1359 #endif
1360 #define delegate_to_filter delegate_to_filter_ocaml
1361 #define filtered_successors filtered_successors_ocaml
1362 #define filtered_predecessors filtered_predecessors_ocaml
1363 #else
1364 #error help
1365 #endif
1366 #endif
1367  struct delegate_to_filter_ctx
1368  {
1369  predsucc_filter &filter;
1370  result res;
1371  delegate_to_filter_ctx(predsucc_filter &flt)
1372  : filter(flt)
1373  , res(result::SUCCESS){}
1374  };
1375  static cs_ps_return_type delegate_to_filter(cs_pdg_vertex v, void *ctx);
1376  public:
1377 
1378 #if defined(SWIG) && defined(SWIGPYTHON)
1379  %exception filtered_predecessors{
1380  try { $action }
1381  catch (cs::filtered_predsucc_swig_exc &) {
1382  SWIG_fail;
1383  }
1384  }
1385  %exception filtered_successors{
1386  try { $action }
1387  catch (cs::filtered_predsucc_swig_exc &) {
1388  SWIG_fail;
1389  }
1390  }
1391  %feature("shadow") filtered_predecessors %{
1392  def filtered_predecessors(self, flt, *args):
1393  if not callable(flt):
1394  return $action(self, flt, *args)
1395  return $action(self, _predsucc_filter2(flt), *args)%}
1396  %feature("shadow") filtered_successors %{
1397  def filtered_successors(self, flt, *args):
1398  if not callable(flt):
1399  return $action(self, flt, *args)
1400  return $action(self, _predsucc_filter2(flt), *args)%}
1401 #endif
1402 
1403 #if defined(SWIG) && defined(SWIGJAVA)
1404  %javaexception("java.lang.Exception") filtered_predecessors {
1405  try { $action }
1406  CATCH_CS_RESULT()
1407  catch (cs::filtered_predsucc_swig_exc &) {
1408  /* exception has already been thrown, we just need to unwind
1409  * the stack.
1410  */
1411  return $null;
1412  }
1413  }
1414  %javaexception("java.lang.Exception") filtered_successors {
1415  try { $action }
1416  CATCH_CS_RESULT()
1417  catch (cs::filtered_predsucc_swig_exc &) {
1418  /* exception has already been thrown, we just need to unwind
1419  * the stack.
1420  */
1421  return $null;
1422  }
1423  }
1424 #endif
1425 
1426 #if defined(SWIG) && defined(SWIGCSHARP)
1427  %typemap(cscode) set_mixin<point_set> %{
1428  public filtered_predsucc_return_pair filtered_successors(predsucc_filter filter, pdg_edge_kind kind) {
1429  filter.clear_exception();
1430  filtered_predsucc_return_pair rv = _filtered_successors(filter, kind);
1431  filter.check_exception();
1432  return rv;
1433  }
1434  public filtered_predsucc_return_pair filtered_predecessors(predsucc_filter filter, pdg_edge_kind kind) {
1435  filter.clear_exception();
1436  filtered_predsucc_return_pair rv = _filtered_predecessors(filter, kind);
1437  filter.check_exception();
1438  return rv;
1439  }
1440  %}
1441 #endif
1442 
1500  filtered_predsucc_return_pair filtered_predecessors(
1501  predsucc_filter &filter,
1502  pdg_edge_kind kind = pdg_edge_kind::DATACONTROL,
1503  size_t threshold = 0,
1504  bool once = false )
1505  {
1506  cs_pdg_vertex_set rv;
1507  bool bool_rv = false;
1508  delegate_to_filter_ctx ctx(filter);
1509  cs_result r = cs_f_pred(
1510  unwrap(),
1511  /* XXX: Does this need to be extern "C"? */
1512  delegate_to_filter,
1513  &ctx,
1514  cglue<pdg_edge_kind>::unwrap(kind),
1515  threshold,
1516  once,
1517  &rv);
1518  if( r == CS_TRUNCATED )
1519  {
1520  bool_rv = true;
1521  r = CS_SUCCESS;
1522  }
1523 #if !CS_CPP_NO_EXCEPTIONS
1524  if( ctx.res == result::ERROR_FOREIGN_EXCEPTION )
1525  /* This exception has to have a distinct type from
1526  * result so that the wrong catch doesn't catch it. */
1527  throw filtered_predsucc_swig_exc();
1528 #endif
1529  ctx.res.check();
1530  check(r);
1531  return std::make_pair(cglue<point_set>::wrap(rv, false), bool_rv);
1532  }
1594  filtered_predsucc_return_pair filtered_successors(
1595  predsucc_filter &filter,
1596  pdg_edge_kind kind = pdg_edge_kind::DATACONTROL,
1597  size_t threshold = 0,
1598  bool once = false )
1599  {
1600  cs_pdg_vertex_set rv;
1601  bool bool_rv = false;
1602  delegate_to_filter_ctx ctx(filter);
1603  cs_result r = cs_f_succ(
1604  unwrap(),
1605  /* XXX: Does this need to be extern "C"? */
1606  delegate_to_filter,
1607  &ctx,
1608  cglue<pdg_edge_kind>::unwrap(kind),
1609  threshold,
1610  once,
1611  &rv);
1612  if( r == CS_TRUNCATED )
1613  {
1614  bool_rv = true;
1615  r = CS_SUCCESS;
1616  }
1617 #if !CS_CPP_NO_EXCEPTIONS
1618  if( ctx.res == result::ERROR_FOREIGN_EXCEPTION )
1619  /* This exception has to have a distinct type from
1620  * result so that the wrong catch doesn't catch it. */
1621  throw filtered_predsucc_swig_exc();
1622 #endif
1623  ctx.res.check();
1624  check(r);
1625  return std::make_pair(cglue<point_set>::wrap(rv, false), bool_rv);
1626  }
1628  };
1629 
1630 }
1631 
1632 #endif /* CS_POINT_SET_DECL_HPP */
Namespace for CodeSonar/CodeSurfer API.
Definition: cs_ast.hpp:33
std::pair< cs::point, std::string > exceptional_return
A std::pair (p, s) where:
Definition: cs_tplt_instantiations.hpp:394
static const result SUCCESS
The function was successful.
Definition: cs_result_decls.hpp:21
std::pair< cs::compunit, cs::point_set > compunit_points_pair
A std::pair (c, ps) where:
Definition: cs_tplt_instantiations.hpp:342
std::vector< cs::compunit_points > compunit_points_vector
A std::vector of compunit_points.
Definition: cs_tplt_instantiations.hpp:354
std::pair< cs::compunit, cs::procedure_and_points_vector > compunit_points
A std::pair (c, ppv) where:
Definition: cs_tplt_instantiations.hpp:332
static const result ERROR_FOREIGN_EXCEPTION
An exception occured in a non-native language.
Definition: cs_result.hpp:572
std::vector< cs::compunit_points_pair > compunit_points_pair_vector
A std::vector of compunit_points_pair.
Definition: cs_tplt_instantiations.hpp:347
std::pair< csuint32, csuint32 > int_pair
A std::pair of integers, used to represent a range of locations in a compilation unit.
Definition: cs_tplt_instantiations.hpp:70