mdds
types.hpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 // SPDX-FileCopyrightText: 2021 - 2025 Kohei Yoshida
4 //
5 // SPDX-License-Identifier: MIT
6 
7 #pragma once
8 
9 #include "../global.hpp"
10 #include "./types_util.hpp"
11 #include "./delayed_delete_vector.hpp"
12 
13 #include <algorithm>
14 #include <cassert>
15 #include <memory>
16 #include <cstdint>
17 #include <vector>
18 #include <sstream>
19 
20 #if defined(MDDS_UNIT_TEST) || defined(MDDS_MULTI_TYPE_VECTOR_DEBUG)
21 #include <iostream>
22 #endif
23 
24 namespace mdds { namespace mtv {
25 
26 using element_t = int;
27 
28 constexpr element_t element_type_empty = -1;
29 
30 constexpr element_t element_type_reserved_start = 0;
31 constexpr element_t element_type_reserved_end = 49;
32 
33 constexpr element_t element_type_user_start = 50;
34 
41 enum class lu_factor_t : int
42 {
43  none = 0,
44  lu4 = 4,
45  lu8 = 8,
46  lu16 = 16,
47  lu32 = 32,
48  sse2_x64 = 1 << 8,
49  sse2_x64_lu4 = 1 << 8 | 4,
50  sse2_x64_lu8 = 1 << 8 | 8,
51  sse2_x64_lu16 = 1 << 8 | 16,
52  avx2_x64 = 2 << 8,
53  avx2_x64_lu4 = 2 << 8 | 4,
54  avx2_x64_lu8 = 2 << 8 | 8,
55 };
56 
76 enum class trace_method_t : int
77 {
78  unspecified = 0,
79  accessor = 1,
80  accessor_with_pos_hint = 1 << 8 | 1,
81  mutator = 2,
82  mutator_with_pos_hint = 1 << 8 | 2,
83  constructor = 3,
84  destructor = 4
85 };
86 
91 {
92  trace_method_t type = trace_method_t::unspecified;
93 
99  const void* instance = nullptr;
100 
102  const char* function_name = nullptr;
103 
108  std::string function_args;
109 
111  const char* filepath = nullptr;
112 
114  int line_number = -1;
115 };
116 
121 {
122 public:
123  element_block_error(const std::string& msg) : mdds::general_error(msg)
124  {}
125 };
126 
127 class base_element_block;
128 element_t get_block_type(const base_element_block&) noexcept;
129 
135 {
136  friend element_t get_block_type(const base_element_block&) noexcept;
137 
138 protected:
139  element_t type;
140  base_element_block(element_t _t) noexcept : type(_t)
141  {}
142 };
143 
147 template<typename ValueT>
148 struct clone_value;
149 
156 template<typename BlockT, typename = void>
158 {
159  using store_type = typename BlockT::store_type;
160 
161  BlockT* operator()(const BlockT& blk) const
162  {
163  auto bt = get_block_type(blk);
164  std::ostringstream os;
165  os << "cloning of this block type is not implemented (type=" << bt << ")";
166  throw element_block_error(os.str());
167  }
168 };
169 
175 template<typename BlockT>
176 struct clone_block<BlockT, std::enable_if_t<std::is_copy_constructible<BlockT>::value>>
177 {
178  using store_type = typename BlockT::store_type;
179 
180  BlockT* operator()(const BlockT& src) const
181  {
182  return new BlockT(src);
183  }
184 };
185 
192 template<typename BlockT>
193 struct clone_block<BlockT, std::void_t<decltype(clone_value<typename BlockT::value_type>{})>>
194 {
195  using store_type = typename BlockT::store_type;
196  using value_type = typename BlockT::value_type;
197  using CV = clone_value<value_type>;
198 
199  BlockT* operator()(const BlockT& src) const
200  {
201  auto dest_blk = std::make_unique<BlockT>();
202  auto cloned(src.store());
203 
205  std::transform(typename CV::exec_policy{}, cloned.begin(), cloned.end(), cloned.begin(), CV{});
206  else
207  std::transform(cloned.begin(), cloned.end(), cloned.begin(), CV{});
208 
209  dest_blk->store().swap(cloned);
210  return dest_blk.release();
211  }
212 };
213 
214 template<typename Self, element_t TypeId, typename ValueT, template<typename, typename> class StoreT>
216 {
217 public:
218  using store_type = StoreT<ValueT, std::allocator<ValueT>>;
219  static constexpr element_t block_type = TypeId;
220 
221 protected:
222  store_type m_array;
223 
224  element_block() noexcept(std::is_nothrow_default_constructible_v<store_type>) : base_element_block(TypeId)
225  {}
226  element_block(size_t n) : base_element_block(TypeId), m_array(n)
227  {}
228  element_block(size_t n, const ValueT& val) : base_element_block(TypeId), m_array(n, val)
229  {}
230 
231  template<typename Iter>
232  element_block(const Iter& it_begin, const Iter& it_end) : base_element_block(TypeId), m_array(it_begin, it_end)
233  {}
234 
235 public:
236  typedef typename store_type::iterator iterator;
237  typedef typename store_type::reverse_iterator reverse_iterator;
238  typedef typename store_type::const_iterator const_iterator;
239  typedef typename store_type::const_reverse_iterator const_reverse_iterator;
240  typedef ValueT value_type;
241 
242  const store_type& store() const noexcept
243  {
244  return m_array;
245  }
246 
247  store_type& store() noexcept
248  {
249  return m_array;
250  }
251 
252 private:
253  template<bool Mutable>
254  class base_range_type
255  {
256  using elem_block_type = mdds::detail::mutable_t<base_element_block, Mutable>;
257  elem_block_type& m_blk;
258 
259  public:
260  using iter_type = std::conditional_t<Mutable, iterator, const_iterator>;
261 
262  base_range_type(elem_block_type& blk) : m_blk(blk)
263  {}
264 
265  iter_type begin()
266  {
267  return element_block::begin(m_blk);
268  }
269 
270  iter_type end()
271  {
272  return element_block::end(m_blk);
273  }
274  };
275 
276  static constexpr bool nothrow_eq_comparable_v = noexcept(std::declval<store_type>() == std::declval<store_type>());
277 
278 public:
279  using range_type = base_range_type<true>;
280  using const_range_type = base_range_type<false>;
281 
282  bool operator==(const Self& r) const noexcept(nothrow_eq_comparable_v)
283  {
284  return m_array == r.m_array;
285  }
286 
287  bool operator!=(const Self& r) const noexcept(nothrow_eq_comparable_v)
288  {
289  return !operator==(r);
290  }
291 
292  static const value_type& at(const base_element_block& block, typename store_type::size_type pos)
293  {
294  return get(block).m_array.at(pos);
295  }
296 
297  static value_type& at(base_element_block& block, typename store_type::size_type pos)
298  {
299  return get(block).m_array.at(pos);
300  }
301 
302  static value_type* data(base_element_block& block)
303  {
304  return get(block).m_array.data();
305  }
306 
307  static typename store_type::size_type size(const base_element_block& block)
308  {
309  return get(block).m_array.size();
310  }
311 
312  static iterator begin(base_element_block& block)
313  {
314  return get(block).m_array.begin();
315  }
316 
317  static iterator end(base_element_block& block)
318  {
319  return get(block).m_array.end();
320  }
321 
322  static const_iterator begin(const base_element_block& block)
323  {
324  return get(block).m_array.begin();
325  }
326 
327  static const_iterator end(const base_element_block& block)
328  {
329  return get(block).m_array.end();
330  }
331 
332  static const_iterator cbegin(const base_element_block& block)
333  {
334  return get(block).m_array.begin();
335  }
336 
337  static const_iterator cend(const base_element_block& block)
338  {
339  return get(block).m_array.end();
340  }
341 
342  static reverse_iterator rbegin(base_element_block& block)
343  {
344  return get(block).m_array.rbegin();
345  }
346 
347  static reverse_iterator rend(base_element_block& block)
348  {
349  return get(block).m_array.rend();
350  }
351 
352  static const_reverse_iterator rbegin(const base_element_block& block)
353  {
354  return get(block).m_array.rbegin();
355  }
356 
357  static const_reverse_iterator rend(const base_element_block& block)
358  {
359  return get(block).m_array.rend();
360  }
361 
362  static const_reverse_iterator crbegin(const base_element_block& block)
363  {
364  return get(block).m_array.rbegin();
365  }
366 
367  static const_reverse_iterator crend(const base_element_block& block)
368  {
369  return get(block).m_array.rend();
370  }
371 
372  static const_range_type range(const base_element_block& block)
373  {
374  return const_range_type(block);
375  }
376 
377  static range_type range(base_element_block& block)
378  {
379  return range_type(block);
380  }
381 
382  static Self& get(base_element_block& block)
383  {
384 #ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
385  if (get_block_type(block) != TypeId)
386  {
387  std::ostringstream os;
388  os << "incorrect block type: expected block type=" << TypeId
389  << ", passed block type=" << get_block_type(block);
390  throw general_error(os.str());
391  }
392 #endif
393  return static_cast<Self&>(block);
394  }
395 
396  static const Self& get(const base_element_block& block)
397  {
398 #ifdef MDDS_MULTI_TYPE_VECTOR_DEBUG
399  if (get_block_type(block) != TypeId)
400  {
401  std::ostringstream os;
402  os << "incorrect block type: expected block type=" << TypeId
403  << ", passed block type=" << get_block_type(block);
404  throw general_error(os.str());
405  }
406 #endif
407  return static_cast<const Self&>(block);
408  }
409 
410  static void set_value(base_element_block& blk, size_t pos, const ValueT& val)
411  {
412  get(blk).m_array[pos] = val;
413  }
414 
415  static void get_value(const base_element_block& blk, size_t pos, ValueT& val)
416  {
417  val = get(blk).m_array[pos];
418  }
419 
420  static value_type get_value(const base_element_block& blk, size_t pos)
421  {
422  return get(blk).m_array[pos];
423  }
424 
425  template<typename T = ValueT>
426  static void append_value(base_element_block& blk, T&& val)
427  {
428  get(blk).m_array.push_back(std::forward<T>(val));
429  }
430 
431  template<typename... Args>
432  static void emplace_back_value(base_element_block& blk, Args&&... args)
433  {
434  get(blk).m_array.emplace_back(std::forward<Args>(args)...);
435  }
436 
437  static void prepend_value(base_element_block& blk, const ValueT& val)
438  {
439  store_type& blk2 = get(blk).m_array;
440  blk2.insert(blk2.begin(), val);
441  }
442 
443  static Self* create_block(size_t init_size)
444  {
445  return new Self(init_size);
446  }
447 
448  static void delete_block(const base_element_block* p)
449  {
450  delete static_cast<const Self*>(p);
451  }
452 
453  static void resize_block(base_element_block& blk, size_t new_size)
454  {
455  store_type& st = get(blk).m_array;
456  st.resize(new_size);
457 
458  // Test if the vector's capacity is larger than twice its current
459  // size, and if so, shrink its capacity to free up some memory.
460  if (new_size < (detail::get_block_capacity(st) / 2))
461  detail::shrink_to_fit(st);
462  }
463 
464 #ifdef MDDS_UNIT_TEST
465  static void print_block(const base_element_block& blk)
466  {
467  const store_type& blk2 = get(blk).m_array;
468  for (const auto& val : blk2)
469  std::cout << val << " ";
470 
471  std::cout << std::endl;
472  }
473 #else
474  static void print_block(const base_element_block&)
475  {}
476 #endif
477 
478  static void erase_value(base_element_block& blk, size_t pos)
479  {
480  store_type& blk2 = get(blk).m_array;
481  blk2.erase(blk2.begin() + pos);
482  }
483 
484  static void erase_values(base_element_block& blk, size_t pos, size_t size)
485  {
486  store_type& blk2 = get(blk).m_array;
487  blk2.erase(blk2.begin() + pos, blk2.begin() + pos + size);
488  }
489 
490  static void append_block(base_element_block& dest, const base_element_block& src)
491  {
492  store_type& d = get(dest).m_array;
493  const store_type& s = get(src).m_array;
494  d.insert(d.end(), s.begin(), s.end());
495  }
496 
497  static void append_values_from_block(
498  base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
499  {
500  store_type& d = get(dest).m_array;
501  const store_type& s = get(src).m_array;
502  std::pair<const_iterator, const_iterator> its = get_iterator_pair(s, begin_pos, len);
503  detail::reserve(d, d.size() + len);
504  d.insert(d.end(), its.first, its.second);
505  }
506 
507  static void assign_values_from_block(
508  base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
509  {
510  store_type& d = get(dest).m_array;
511  const store_type& s = get(src).m_array;
512  std::pair<const_iterator, const_iterator> its = get_iterator_pair(s, begin_pos, len);
513  d.assign(its.first, its.second);
514  }
515 
516  static void prepend_values_from_block(
517  base_element_block& dest, const base_element_block& src, size_t begin_pos, size_t len)
518  {
519  store_type& d = get(dest).m_array;
520  const store_type& s = get(src).m_array;
521  std::pair<const_iterator, const_iterator> its = get_iterator_pair(s, begin_pos, len);
522  detail::reserve(d, d.size() + len);
523  d.insert(d.begin(), its.first, its.second);
524  }
525 
526  static void swap_values(base_element_block& blk1, base_element_block& blk2, size_t pos1, size_t pos2, size_t len)
527  {
528  store_type& st1 = get(blk1).m_array;
529  store_type& st2 = get(blk2).m_array;
530  assert(pos1 + len <= st1.size());
531  assert(pos2 + len <= st2.size());
532 
533  typename store_type::iterator it1 = st1.begin(), it2 = st2.begin();
534  std::advance(it1, pos1);
535  std::advance(it2, pos2);
536 
537  for (size_t i = 0; i < len; ++i, ++it1, ++it2)
538  {
539  value_type v1 = *it1, v2 = *it2;
540  *it1 = v2;
541  *it2 = v1;
542  }
543  }
544 
545  static bool equal_block(const base_element_block& left, const base_element_block& right)
546  {
547  return get(left) == get(right);
548  }
549 
550  template<typename Iter>
551  static void set_values(base_element_block& block, size_t pos, const Iter& it_begin, const Iter& it_end)
552  {
553  store_type& d = get(block).m_array;
554  typename store_type::iterator it_dest = d.begin();
555  std::advance(it_dest, pos);
556  for (Iter it = it_begin; it != it_end; ++it, ++it_dest)
557  *it_dest = *it;
558  }
559 
560  template<typename Iter>
561  static void append_values(base_element_block& block, const Iter& it_begin, const Iter& it_end)
562  {
563  store_type& d = get(block).m_array;
564  typename store_type::iterator it = d.end();
565  d.insert(it, it_begin, it_end);
566  }
567 
568  template<typename Iter>
569  static void prepend_values(base_element_block& block, const Iter& it_begin, const Iter& it_end)
570  {
571  store_type& d = get(block).m_array;
572  d.insert(d.begin(), it_begin, it_end);
573  }
574 
575  template<typename Iter>
576  static void assign_values(base_element_block& dest, const Iter& it_begin, const Iter& it_end)
577  {
578  store_type& d = get(dest).m_array;
579  d.assign(it_begin, it_end);
580  }
581 
582  template<typename Iter>
583  static void insert_values(base_element_block& block, size_t pos, const Iter& it_begin, const Iter& it_end)
584  {
585  store_type& blk = get(block).m_array;
586  blk.insert(blk.begin() + pos, it_begin, it_end);
587  }
588 
589  static size_t capacity(const base_element_block& block)
590  {
591  const store_type& blk = get(block).m_array;
592  return detail::get_block_capacity(blk);
593  }
594 
595  static void reserve(base_element_block& block, std::size_t size)
596  {
597  store_type& blk = get(block).m_array;
598  detail::reserve(blk, size);
599  }
600 
601  static void shrink_to_fit(base_element_block& block)
602  {
603  store_type& blk = get(block).m_array;
604  detail::shrink_to_fit(blk);
605  }
606 
607 private:
608  static std::pair<const_iterator, const_iterator> get_iterator_pair(
609  const store_type& array, size_t begin_pos, size_t len)
610  {
611  assert(begin_pos + len <= array.size());
612  const_iterator it = array.begin();
613  std::advance(it, begin_pos);
614  const_iterator it_end = it;
615  std::advance(it_end, len);
616  return std::pair<const_iterator, const_iterator>(it, it_end);
617  }
618 };
619 
620 template<typename Self, element_t TypeId, typename ValueT, template<typename, typename> class StoreT>
621 class copyable_element_block : public element_block<Self, TypeId, ValueT, StoreT>
622 {
624 
625 protected:
626  copyable_element_block() noexcept(std::is_nothrow_default_constructible_v<base_type>) : base_type()
627  {}
628  copyable_element_block(size_t n) : base_type(n)
629  {}
630  copyable_element_block(size_t n, const ValueT& val) : base_type(n, val)
631  {}
632 
633  template<typename Iter>
634  copyable_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
635  {}
636 
637 public:
638  using base_type::get;
639 
640  static Self* copy_block(const base_element_block& blk)
641  {
642  // Use copy constructor to copy the data.
643  return new Self(get(blk));
644  }
645 
646  static Self* clone_block(const base_element_block& src)
647  {
648  return mdds::mtv::clone_block<Self>{}(get(src));
649  }
650 };
651 
655 template<typename Self, element_t TypeId, typename ValueT, template<typename, typename> class StoreT>
656 class noncopyable_element_block : public element_block<Self, TypeId, ValueT, StoreT>
657 {
659 
660 protected:
661  noncopyable_element_block() noexcept(std::is_nothrow_default_constructible_v<base_type>) : base_type()
662  {}
663  noncopyable_element_block(size_t n) : base_type(n)
664  {}
665  noncopyable_element_block(size_t n, const ValueT& val) : base_type(n, val)
666  {}
667 
668  template<typename Iter>
669  noncopyable_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
670  {}
671 
672 public:
673  using base_type::get;
674 
676  noncopyable_element_block& operator=(const noncopyable_element_block&) = delete;
677 
678  static Self* copy_block(const base_element_block&)
679  {
680  throw element_block_error("attempted to copy a noncopyable element block.");
681  }
682 
683  static Self* clone_block(const base_element_block& src)
684  {
685  return mdds::mtv::clone_block<Self>{}(get(src));
686  }
687 };
688 
696 inline element_t get_block_type(const base_element_block& blk) noexcept
697 {
698  return blk.type;
699 }
700 
705 template<element_t TypeId, typename ValueT, template<typename, typename> class StoreT = delayed_delete_vector>
707  : public copyable_element_block<default_element_block<TypeId, ValueT, StoreT>, TypeId, ValueT, StoreT>
708 {
711 
712  default_element_block() noexcept(std::is_nothrow_default_constructible_v<base_type>) : base_type()
713  {}
714  default_element_block(size_t n) : base_type(n)
715  {}
716  default_element_block(size_t n, const ValueT& val) : base_type(n, val)
717  {}
718 
719  template<typename Iter>
720  default_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
721  {}
722 
723  static self_type* create_block_with_value(size_t init_size, const ValueT& val)
724  {
725  return new self_type(init_size, val);
726  }
727 
728  template<typename Iter>
729  static self_type* create_block_with_values(const Iter& it_begin, const Iter& it_end)
730  {
731  return new self_type(it_begin, it_end);
732  }
733 
734  static void overwrite_values(base_element_block&, size_t, size_t)
735  {
736  // Do nothing.
737  }
738 };
739 
744 template<element_t TypeId, typename ValueT, template<typename, typename> class StoreT = delayed_delete_vector>
746  : public copyable_element_block<managed_element_block<TypeId, ValueT, StoreT>, TypeId, ValueT*, StoreT>
747 {
750 
751  using base_type::get;
752  using base_type::m_array;
753  using base_type::reserve;
754  using base_type::set_value;
755 
756  managed_element_block() noexcept(std::is_nothrow_default_constructible_v<base_type>) : base_type()
757  {}
758  managed_element_block(size_t n) : base_type(n)
759  {}
761  {
762  detail::reserve(m_array, r.m_array.size());
763  for (const auto& v : r.m_array)
764  m_array.push_back(new ValueT(*v));
765  }
766 
767  template<typename Iter>
768  managed_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
769  {}
770 
772  {
773  std::for_each(m_array.begin(), m_array.end(), std::default_delete<ValueT>());
774  }
775 
776  static self_type* create_block_with_value(size_t init_size, ValueT* val)
777  {
778  // Managed blocks don't support initialization with value.
779  if (init_size > 1)
780  throw general_error("You can't create a managed block with initial value.");
781 
782  std::unique_ptr<self_type> blk = std::make_unique<self_type>(init_size);
783  if (init_size == 1)
784  set_value(*blk, 0, val);
785 
786  return blk.release();
787  }
788 
789  template<typename Iter>
790  static self_type* create_block_with_values(const Iter& it_begin, const Iter& it_end)
791  {
792  return new self_type(it_begin, it_end);
793  }
794 
795  static void overwrite_values(base_element_block& block, size_t pos, size_t len)
796  {
797  managed_element_block& blk = get(block);
798  typename managed_element_block::store_type::iterator it = blk.m_array.begin() + pos;
799  typename managed_element_block::store_type::iterator it_end = it + len;
800  std::for_each(it, it_end, std::default_delete<ValueT>());
801  }
802 };
803 
809 template<element_t TypeId, typename ValueT, template<typename, typename> class StoreT = delayed_delete_vector>
811  : public noncopyable_element_block<
812  noncopyable_managed_element_block<TypeId, ValueT, StoreT>, TypeId, ValueT*, StoreT>
813 {
816 
817  using base_type::get;
818  using base_type::m_array;
819  using base_type::set_value;
820 
821  noncopyable_managed_element_block() noexcept(std::is_nothrow_default_constructible_v<base_type>) : base_type()
822  {}
824  {}
825 
826  template<typename Iter>
827  noncopyable_managed_element_block(const Iter& it_begin, const Iter& it_end) : base_type(it_begin, it_end)
828  {}
829 
831  {
832  std::for_each(m_array.begin(), m_array.end(), std::default_delete<ValueT>());
833  }
834 
835  static self_type* create_block_with_value(size_t init_size, ValueT* val)
836  {
837  // Managed blocks don't support initialization with value.
838  if (init_size > 1)
839  throw general_error("You can't create a managed block with initial value.");
840 
841  std::unique_ptr<self_type> blk = std::make_unique<self_type>(init_size);
842  if (init_size == 1)
843  set_value(*blk, 0, val);
844 
845  return blk.release();
846  }
847 
848  template<typename Iter>
849  static self_type* create_block_with_values(const Iter& it_begin, const Iter& it_end)
850  {
851  return new self_type(it_begin, it_end);
852  }
853 
854  static void overwrite_values(base_element_block& block, size_t pos, size_t len)
855  {
856  noncopyable_managed_element_block& blk = get(block);
857  typename noncopyable_managed_element_block::store_type::iterator it = blk.m_array.begin() + pos;
858  typename noncopyable_managed_element_block::store_type::iterator it_end = it + len;
859  std::for_each(it, it_end, std::default_delete<ValueT>());
860  }
861 
862  static bool equal_block(const base_element_block& left, const base_element_block& right)
863  {
864  const auto& array1 = get(left).m_array;
865  const auto& array2 = get(right).m_array;
866 
867  if (array1.size() != array2.size())
868  return false;
869 
870  return std::equal(array1.begin(), array1.end(), array2.begin(), [](const ValueT* p1, const ValueT* p2) {
871  return *p1 == *p2;
872  });
873  }
874 };
875 
876 namespace detail {
877 
878 template<typename Blk>
879 bool get_block_element_at(const mdds::mtv::base_element_block& data, size_t offset, std::true_type)
880 {
881  auto it = Blk::cbegin(data);
882  std::advance(it, offset);
883  return *it;
884 }
885 
886 template<typename Blk>
887 typename Blk::value_type get_block_element_at(const mdds::mtv::base_element_block& data, size_t offset, std::false_type)
888 {
889  return Blk::at(data, offset);
890 }
891 
892 template<typename Blk>
893 typename Blk::value_type get_block_element_at(const mdds::mtv::base_element_block& data, size_t offset)
894 {
895  typename mdds::mtv::detail::has_std_vector_bool_store<Blk>::type v;
896  return get_block_element_at<Blk>(data, offset, v);
897 }
898 
899 } // namespace detail
900 
901 }} // namespace mdds::mtv
902 
903 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Definition: types.hpp:120
const char * filepath
Definition: types.hpp:111
Definition: types.hpp:148
friend element_t get_block_type(const base_element_block &) noexcept
Definition: types.hpp:696
std::string function_args
Definition: types.hpp:108
Definition: types.hpp:621
Definition: types.hpp:90
Definition: types.hpp:745
int line_number
Definition: types.hpp:114
const void * instance
Definition: types.hpp:99
Definition: types.hpp:215
Definition: types.hpp:706
Definition: types.hpp:157
Definition: global.hpp:59
const char * function_name
Definition: types.hpp:102
Definition: types_util.hpp:142
Definition: cref_wrapper.hpp:12
Definition: types.hpp:656
Definition: delayed_delete_vector.hpp:21
Definition: types.hpp:134