Directory: | ./ |
---|---|
File: | pdcom5/src/Variable.cpp |
Date: | 2024-11-17 04:08:36 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 32 | 60 | 53.3% |
Branches: | 45 | 438 | 10.3% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /***************************************************************************** | ||
2 | * | ||
3 | * Copyright (C) 2015-2016 Richard Hacker (lerichi at gmx dot net) | ||
4 | * | ||
5 | * This file is part of the PdCom library. | ||
6 | * | ||
7 | * The PdCom library is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU Lesser General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | * The PdCom library is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | ||
15 | * License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public License | ||
18 | * along with the PdCom library. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | *****************************************************************************/ | ||
21 | #include "Variable.h" | ||
22 | |||
23 | #include "Debug.h" | ||
24 | #include "Future.h" | ||
25 | #include "Process.h" | ||
26 | #include "Selector.h" | ||
27 | #include "TemplateVodoo.inc" | ||
28 | |||
29 | #include <algorithm> | ||
30 | #include <array> | ||
31 | #include <cassert> | ||
32 | #include <numeric> | ||
33 | #include <pdcom5/Subscriber.h> | ||
34 | #include <pdcom5/Subscription.h> | ||
35 | #include <pdcom5/Variable.h> | ||
36 | #include <sstream> | ||
37 | #include <stdint.h> | ||
38 | |||
39 | namespace { | ||
40 | |||
41 | using copyFn = void (*)(void *, const void *, size_t, size_t); | ||
42 | |||
43 | template < | ||
44 | PdCom::TypeInfo::DataType dest_type, | ||
45 | PdCom::TypeInfo::DataType src_type> | ||
46 | struct DataConverter | ||
47 | { | ||
48 | using Src = typename PdCom::details::DataTypeTraits<src_type>::value_type; | ||
49 | using Dest = typename PdCom::details::DataTypeTraits<dest_type>::value_type; | ||
50 | |||
51 | static void | ||
52 | 92 | copyData(void *_dest, const void *_src, size_t nelem, size_t offset) | |
53 | { | ||
54 | 92 | auto dest = reinterpret_cast<Dest *>(_dest); | |
55 | 92 | auto src = reinterpret_cast<const Src *>(_src) + offset; | |
56 |
12/288✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✓ Branch 52 taken 22 times.
✓ Branch 53 taken 22 times.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 99 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 105 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✗ Branch 116 not taken.
✗ Branch 117 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
✗ Branch 120 not taken.
✗ Branch 121 not taken.
✗ Branch 122 not taken.
✗ Branch 123 not taken.
✓ Branch 124 taken 9 times.
✓ Branch 125 taken 9 times.
✓ Branch 126 taken 64 times.
✓ Branch 127 taken 20 times.
✗ Branch 128 not taken.
✗ Branch 129 not taken.
✓ Branch 130 taken 71 times.
✓ Branch 131 taken 35 times.
✗ Branch 132 not taken.
✗ Branch 133 not taken.
✓ Branch 134 taken 4 times.
✓ Branch 135 taken 4 times.
✗ Branch 136 not taken.
✗ Branch 137 not taken.
✗ Branch 138 not taken.
✗ Branch 139 not taken.
✗ Branch 140 not taken.
✗ Branch 141 not taken.
✗ Branch 142 not taken.
✗ Branch 143 not taken.
✗ Branch 144 not taken.
✗ Branch 145 not taken.
✗ Branch 146 not taken.
✗ Branch 147 not taken.
✗ Branch 148 not taken.
✗ Branch 149 not taken.
✗ Branch 150 not taken.
✗ Branch 151 not taken.
✗ Branch 152 not taken.
✗ Branch 153 not taken.
✗ Branch 154 not taken.
✗ Branch 155 not taken.
✓ Branch 156 taken 2 times.
✓ Branch 157 taken 2 times.
✗ Branch 158 not taken.
✗ Branch 159 not taken.
✗ Branch 160 not taken.
✗ Branch 161 not taken.
✗ Branch 162 not taken.
✗ Branch 163 not taken.
✗ Branch 164 not taken.
✗ Branch 165 not taken.
✗ Branch 166 not taken.
✗ Branch 167 not taken.
✗ Branch 168 not taken.
✗ Branch 169 not taken.
✗ Branch 170 not taken.
✗ Branch 171 not taken.
✗ Branch 172 not taken.
✗ Branch 173 not taken.
✗ Branch 174 not taken.
✗ Branch 175 not taken.
✗ Branch 176 not taken.
✗ Branch 177 not taken.
✗ Branch 178 not taken.
✗ Branch 179 not taken.
✗ Branch 180 not taken.
✗ Branch 181 not taken.
✗ Branch 182 not taken.
✗ Branch 183 not taken.
✗ Branch 184 not taken.
✗ Branch 185 not taken.
✗ Branch 186 not taken.
✗ Branch 187 not taken.
✗ Branch 188 not taken.
✗ Branch 189 not taken.
✗ Branch 190 not taken.
✗ Branch 191 not taken.
✗ Branch 192 not taken.
✗ Branch 193 not taken.
✗ Branch 194 not taken.
✗ Branch 195 not taken.
✗ Branch 196 not taken.
✗ Branch 197 not taken.
✗ Branch 198 not taken.
✗ Branch 199 not taken.
✗ Branch 200 not taken.
✗ Branch 201 not taken.
✗ Branch 202 not taken.
✗ Branch 203 not taken.
✗ Branch 204 not taken.
✗ Branch 205 not taken.
✗ Branch 206 not taken.
✗ Branch 207 not taken.
✗ Branch 208 not taken.
✗ Branch 209 not taken.
✗ Branch 210 not taken.
✗ Branch 211 not taken.
✗ Branch 212 not taken.
✗ Branch 213 not taken.
✗ Branch 214 not taken.
✗ Branch 215 not taken.
✗ Branch 216 not taken.
✗ Branch 217 not taken.
✗ Branch 218 not taken.
✗ Branch 219 not taken.
✗ Branch 220 not taken.
✗ Branch 221 not taken.
✗ Branch 222 not taken.
✗ Branch 223 not taken.
✗ Branch 224 not taken.
✗ Branch 225 not taken.
✗ Branch 226 not taken.
✗ Branch 227 not taken.
✗ Branch 228 not taken.
✗ Branch 229 not taken.
✗ Branch 230 not taken.
✗ Branch 231 not taken.
✗ Branch 232 not taken.
✗ Branch 233 not taken.
✗ Branch 234 not taken.
✗ Branch 235 not taken.
✗ Branch 236 not taken.
✗ Branch 237 not taken.
✗ Branch 238 not taken.
✗ Branch 239 not taken.
✗ Branch 240 not taken.
✗ Branch 241 not taken.
✗ Branch 242 not taken.
✗ Branch 243 not taken.
✗ Branch 244 not taken.
✗ Branch 245 not taken.
✗ Branch 246 not taken.
✗ Branch 247 not taken.
✗ Branch 248 not taken.
✗ Branch 249 not taken.
✗ Branch 250 not taken.
✗ Branch 251 not taken.
✗ Branch 252 not taken.
✗ Branch 253 not taken.
✗ Branch 254 not taken.
✗ Branch 255 not taken.
✗ Branch 256 not taken.
✗ Branch 257 not taken.
✗ Branch 258 not taken.
✗ Branch 259 not taken.
✗ Branch 260 not taken.
✗ Branch 261 not taken.
✗ Branch 262 not taken.
✗ Branch 263 not taken.
✗ Branch 264 not taken.
✗ Branch 265 not taken.
✗ Branch 266 not taken.
✗ Branch 267 not taken.
✗ Branch 268 not taken.
✗ Branch 269 not taken.
✗ Branch 270 not taken.
✗ Branch 271 not taken.
✗ Branch 272 not taken.
✗ Branch 273 not taken.
✗ Branch 274 not taken.
✗ Branch 275 not taken.
✗ Branch 276 not taken.
✗ Branch 277 not taken.
✗ Branch 278 not taken.
✗ Branch 279 not taken.
✗ Branch 280 not taken.
✗ Branch 281 not taken.
✗ Branch 282 not taken.
✗ Branch 283 not taken.
✗ Branch 284 not taken.
✗ Branch 285 not taken.
✗ Branch 286 not taken.
✗ Branch 287 not taken.
|
264 | for (size_t i = 0; i < nelem; ++i) |
57 |
0/24✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
✗ Branch 127 not taken.
✗ Branch 128 not taken.
✗ Branch 153 not taken.
✗ Branch 154 not taken.
✗ Branch 179 not taken.
✗ Branch 180 not taken.
✗ Branch 205 not taken.
✗ Branch 206 not taken.
✗ Branch 231 not taken.
✗ Branch 232 not taken.
✗ Branch 257 not taken.
✗ Branch 258 not taken.
✗ Branch 283 not taken.
✗ Branch 284 not taken.
✗ Branch 309 not taken.
✗ Branch 310 not taken.
|
172 | dest[i] = static_cast<Dest>(src[i]); |
58 | 92 | } | |
59 | }; | ||
60 | |||
61 | template < | ||
62 | PdCom::TypeInfo::DataType dest_type, | ||
63 | PdCom::TypeInfo::DataType... src_types> | ||
64 | constexpr std::array<copyFn, sizeof...(src_types)> | ||
65 | getcopyFnRow(sequence<src_types...>) | ||
66 | { | ||
67 | return {{DataConverter<dest_type, src_types>::copyData...}}; | ||
68 | } | ||
69 | |||
70 | template <PdCom::TypeInfo::DataType... dest_types> | ||
71 | constexpr std:: | ||
72 | array<std::array<copyFn, sizeof...(dest_types)>, sizeof...(dest_types)> | ||
73 | getcopyFnMatrix(sequence<dest_types...> seq) | ||
74 | { | ||
75 | return {{getcopyFnRow<dest_types>(seq)...}}; | ||
76 | } | ||
77 | |||
78 | |||
79 | /** Some comments on the template vodoo: | ||
80 | * DataTypeSequence will expand to a struct sequence<boolean_T, char_T, ..., | ||
81 | * double_T, single_T>. The template arguments of sequence (boolean_T, ..., | ||
82 | * single_T) can then be used to initialize (list initialization) an array by | ||
83 | * applying one function on each of these template arguments. | ||
84 | * This is used to generate the matrix row by row | ||
85 | */ | ||
86 | |||
87 | |||
88 | } // namespace | ||
89 | |||
90 | 92 | void PdCom::details::copyData( | |
91 | void *dst, | ||
92 | TypeInfo::DataType dst_type, | ||
93 | const void *src, | ||
94 | TypeInfo::DataType src_type, | ||
95 | size_t nelem, | ||
96 | size_t offset) | ||
97 | { | ||
98 | using PdCom::TypeInfo; | ||
99 |
2/4✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 92 times.
✗ Branch 3 not taken.
|
92 | if (dst_type < TypeInfo::DataTypeBegin or dst_type >= TypeInfo::DataTypeEnd |
100 |
1/2✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
|
92 | or src_type < TypeInfo::DataTypeBegin |
101 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
|
92 | or src_type >= TypeInfo::DataTypeEnd) |
102 | ✗ | return; | |
103 | |||
104 | static constexpr auto copyMatrix = getcopyFnMatrix(DataTypeSequence {}); | ||
105 | static_assert( | ||
106 | copyMatrix.size() == DataTypeCount, "Copy Matrix size mismatch"); | ||
107 | // first index: dst, second index: sr | ||
108 | |||
109 | 92 | copyMatrix[dst_type][src_type](dst, src, nelem, offset); | |
110 | } | ||
111 | |||
112 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<uint8_t>::type_info; | ||
113 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<uint16_t>::type_info; | ||
114 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<uint32_t>::type_info; | ||
115 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<uint64_t>::type_info; | ||
116 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<int8_t>::type_info; | ||
117 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<int16_t>::type_info; | ||
118 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<int32_t>::type_info; | ||
119 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<int64_t>::type_info; | ||
120 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<bool>::type_info; | ||
121 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<float>::type_info; | ||
122 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<double>::type_info; | ||
123 | constexpr PdCom::TypeInfo PdCom::details::TypeInfoTraits<char>::type_info; | ||
124 | |||
125 | |||
126 | using PdCom::Variable; | ||
127 | |||
128 | 74 | PdCom::SizeInfo Variable::getSizeInfo() const | |
129 | { | ||
130 |
2/4✓ Branch 3 taken 74 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 74 times.
|
74 | if (auto p = pimpl_.lock()) |
131 |
2/4✓ Branch 3 taken 74 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 74 times.
|
148 | return p->size_info; |
132 | ✗ | throw EmptyVariable(); | |
133 | } | ||
134 | |||
135 | 2524 | PdCom::TypeInfo Variable::getTypeInfo() const | |
136 | { | ||
137 |
2/4✓ Branch 3 taken 2524 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2524 times.
|
2524 | if (auto p = pimpl_.lock()) |
138 |
1/2✗ Branch 7 not taken.
✓ Branch 8 taken 2524 times.
|
5048 | return *p->type_info; |
139 | ✗ | throw EmptyVariable(); | |
140 | } | ||
141 | |||
142 | 5 | std::string Variable::getName() const | |
143 | { | ||
144 |
2/4✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
|
5 | if (auto p = pimpl_.lock()) |
145 |
2/4✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 5 times.
|
10 | return p->getName(); |
146 | ✗ | throw EmptyVariable(); | |
147 | } | ||
148 | |||
149 | 419 | std::string Variable::getPath() const | |
150 | { | ||
151 |
2/4✓ Branch 3 taken 419 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 419 times.
|
419 | if (auto p = pimpl_.lock()) |
152 |
2/4✓ Branch 5 taken 419 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 419 times.
|
838 | return p->getPath(); |
153 | ✗ | throw EmptyVariable(); | |
154 | } | ||
155 | |||
156 | 47 | Variable::SetValueFuture Variable::setValue( | |
157 | const void *data, | ||
158 | PdCom::TypeInfo::DataType t, | ||
159 | size_t count, | ||
160 | const PdCom::Selector &selector) const | ||
161 | { | ||
162 |
2/4✓ Branch 3 taken 47 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 43 times.
|
51 | if (auto p = pimpl_.lock()) { |
163 |
2/2✓ Branch 2 taken 12 times.
✓ Branch 3 taken 35 times.
|
47 | if (selector.impl_) |
164 |
1/2✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
|
12 | return selector.impl_->applySetValue(*p, data, t, count); |
165 |
3/4✓ Branch 6 taken 31 times.
✓ Branch 7 taken 4 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 43 times.
|
39 | return PdCom::impl::Selector().applySetValue(*p, data, t, count); |
166 | } | ||
167 | ✗ | throw EmptyVariable(); | |
168 | } | ||
169 | |||
170 | ✗ | Variable::SetValueFuture Variable::setValue( | |
171 | const void *src, | ||
172 | PdCom::TypeInfo::DataType src_type, | ||
173 | size_t count, | ||
174 | size_t offset) const | ||
175 | { | ||
176 | ✗ | if (auto p = pimpl_.lock()) { | |
177 | ✗ | return p->setValue(src, src_type, offset, count); | |
178 | } | ||
179 | ✗ | throw EmptyVariable(); | |
180 | } | ||
181 | |||
182 | ✗ | std::string Variable::getAlias() const | |
183 | { | ||
184 | ✗ | if (auto p = pimpl_.lock()) | |
185 | ✗ | return p->getAlias(); | |
186 | ✗ | throw EmptyVariable(); | |
187 | } | ||
188 | |||
189 | ✗ | int Variable::getTaskId() const | |
190 | { | ||
191 | ✗ | if (auto p = pimpl_.lock()) | |
192 | ✗ | return p->getTaskId(); | |
193 | ✗ | throw EmptyVariable(); | |
194 | } | ||
195 | |||
196 | ✗ | bool Variable::isWriteable() const | |
197 | { | ||
198 | ✗ | if (auto p = pimpl_.lock()) | |
199 | ✗ | return p->isWriteable(); | |
200 | ✗ | throw EmptyVariable(); | |
201 | } | ||
202 | |||
203 | ✗ | std::chrono::duration<double> Variable::getSampleTime() const | |
204 | { | ||
205 | ✗ | if (auto p = pimpl_.lock()) | |
206 | ✗ | return p->getSampleTime(); | |
207 | ✗ | throw EmptyVariable(); | |
208 | } | ||
209 | |||
210 | 1 | PdCom::Process *Variable::getProcess() const | |
211 | { | ||
212 |
2/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
|
1 | if (auto p = pimpl_.lock()) { |
213 |
2/4✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
|
1 | if (const auto process = p->process.lock()) { |
214 |
1/2✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
|
2 | return process->This; |
215 | } | ||
216 | ✗ | throw ProcessGoneAway(); | |
217 | } | ||
218 | ✗ | throw EmptyVariable(); | |
219 | } | ||
220 | |||
221 | template class PdCom::Future< | ||
222 | PdCom::Exception const &, | ||
223 | PdCom::VariablePollResult, | ||
224 | std::chrono::nanoseconds>; | ||
225 | |||
226 | template class PdCom::Future<PdCom::Exception const &>; | ||
227 | |||
228 | using PdCom::details::is_contiguous; | ||
229 | static_assert( | ||
230 | is_contiguous<std::vector<int>>::value, | ||
231 | "vector<int> is contiguous"); | ||
232 | static_assert( | ||
233 | !is_contiguous<std::vector<bool>>::value, | ||
234 | "vector<bool> is not contiguous"); | ||
235 | static_assert( | ||
236 | is_contiguous<std::array<int, 5>>::value, | ||
237 | "array<int> is contiguous"); | ||
238 | static_assert(is_contiguous<std::string>::value, "string is contiguous"); | ||
239 |