KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_net_chain_manual.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21
24
25#include <connection_graph.h>
26#include <kiid.h>
27#include <sch_netchain.h>
28#include <sch_screen.h>
29#include <sch_sheet.h>
30#include <sch_sheet_path.h>
31#include <sch_symbol.h>
32#include <schematic.h>
34#include <locale_io.h>
35
36#include <fstream>
37#include <set>
38
39#include <wx/filename.h>
40#include <wx/filefn.h>
41
42
50
51
52// Force-create a manual net chain via the same backend path the dialog uses when
53// no passthrough connectivity exists between the two terminals. Validates that:
54// - the chain is inserted into m_committedNetChains (not the legacy KIID-keyed map)
55// - it is queryable by GetNetChainByName / GetNetChainForNet
56// - it survives a Recalculate via the terminal-ref restore pass
57// - duplicate names and IsValidName violations are rejected
58BOOST_FIXTURE_TEST_CASE( NetChain_ManualForceCreate_CommitsAndQueryable, NETCHAIN_MANUAL_FIXTURE )
59{
61 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ), m_schematic );
62
63 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
64 BOOST_REQUIRE( graph );
65
66 graph->Recalculate( m_schematic->BuildSheetListSortedByPageNumbers(), true );
67
68 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
69 BOOST_REQUIRE( !sheets.empty() );
70
71 // Find two distinct symbols and one connected pin from each to use as terminals.
72 SCH_SYMBOL* symA = nullptr;
73 SCH_SYMBOL* symB = nullptr;
74 SCH_PIN* pinA = nullptr;
75 SCH_PIN* pinB = nullptr;
76 wxString refA, refB, pinNumA, pinNumB;
77 wxString netA, netB;
78
79 for( const SCH_SHEET_PATH& sp : sheets )
80 {
81 SCH_SCREEN* sc = sp.LastScreen();
82
83 if( !sc )
84 continue;
85
86 for( SCH_ITEM* item : sc->Items().OfType( SCH_SYMBOL_T ) )
87 {
88 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
89
90 for( SCH_PIN* p : sym->GetPins( &sp ) )
91 {
92 if( !p->Connection() || p->Connection()->Name().IsEmpty() )
93 continue;
94
95 if( !symA )
96 {
97 symA = sym;
98 pinA = p;
99 refA = sym->GetRef( &sp );
100 pinNumA = p->GetNumber();
101 netA = p->Connection()->Name();
102 }
103 else if( sym != symA && p->Connection()->Name() != netA )
104 {
105 symB = sym;
106 pinB = p;
107 refB = sym->GetRef( &sp );
108 pinNumB = p->GetNumber();
109 netB = p->Connection()->Name();
110 break;
111 }
112 }
113
114 if( symB )
115 break;
116 }
117
118 if( symB )
119 break;
120 }
121
122 BOOST_REQUIRE( symA );
123 BOOST_REQUIRE( symB );
124 BOOST_REQUIRE( pinA );
125 BOOST_REQUIRE( pinB );
126
127 std::set<SCH_SYMBOL*> symbols { symA, symB };
128 std::set<wxString> nets { netA, netB };
129
130 // IsValidName rejects spaces — verify the API enforces this.
131 SCH_NETCHAIN* rejectedSpace = graph->CreateManualNetChain(
132 wxT( "BAD NAME" ), symbols, nets, pinA->m_Uuid, pinB->m_Uuid, refA, pinNumA, refB, pinNumB );
133 BOOST_CHECK( rejectedSpace == nullptr );
134
135 // Empty names are rejected.
136 SCH_NETCHAIN* rejectedEmpty = graph->CreateManualNetChain(
137 wxEmptyString, symbols, nets, pinA->m_Uuid, pinB->m_Uuid, refA, pinNumA, refB, pinNumB );
138 BOOST_CHECK( rejectedEmpty == nullptr );
139
140 SCH_NETCHAIN* committed = graph->CreateManualNetChain(
141 wxT( "MANUAL_CHAIN" ), symbols, nets, pinA->m_Uuid, pinB->m_Uuid, refA, pinNumA, refB, pinNumB );
142
143 BOOST_REQUIRE( committed );
144 BOOST_CHECK_EQUAL( committed->GetName(), wxString( wxT( "MANUAL_CHAIN" ) ) );
145 BOOST_CHECK_EQUAL( committed->GetNets().count( netA ), 1u );
146 BOOST_CHECK_EQUAL( committed->GetNets().count( netB ), 1u );
147 BOOST_CHECK_EQUAL( committed->GetSymbols().count( symA ), 1u );
148 BOOST_CHECK_EQUAL( committed->GetSymbols().count( symB ), 1u );
149 BOOST_CHECK_EQUAL( committed->GetTerminalRef( 0 ), refA );
150 BOOST_CHECK_EQUAL( committed->GetTerminalPinNum( 0 ), pinNumA );
151 BOOST_CHECK_EQUAL( committed->GetTerminalRef( 1 ), refB );
152 BOOST_CHECK_EQUAL( committed->GetTerminalPinNum( 1 ), pinNumB );
153
154 // Chain must be reachable through the public accessors that IO save, netlist
155 // export, and the Setup panel use.
156 BOOST_CHECK( graph->GetNetChainByName( wxT( "MANUAL_CHAIN" ) ) == committed );
157
158 bool foundInCommittedList = false;
159
160 for( const std::unique_ptr<SCH_NETCHAIN>& chain : graph->GetCommittedNetChains() )
161 {
162 if( chain && chain->GetName() == wxT( "MANUAL_CHAIN" ) )
163 {
164 foundInCommittedList = true;
165 break;
166 }
167 }
168
169 BOOST_CHECK( foundInCommittedList );
170
171 // GetNetChainForNet should resolve member nets to the manual chain.
172 BOOST_CHECK( graph->GetNetChainForNet( netA ) == committed );
173 BOOST_CHECK( graph->GetNetChainForNet( netB ) == committed );
174
175 // Duplicate name must be rejected.
177 wxT( "MANUAL_CHAIN" ), symbols, nets, pinA->m_Uuid, pinB->m_Uuid, refA, pinNumA, refB, pinNumB );
178 BOOST_CHECK( duplicate == nullptr );
179
180 // Member symbols carry the chain marker so the post-Recalculate restore pass
181 // does not reapply a different name.
182 BOOST_CHECK_EQUAL( symA->GetNetChainName(), wxString( wxT( "MANUAL_CHAIN" ) ) );
183 BOOST_CHECK_EQUAL( symB->GetNetChainName(), wxString( wxT( "MANUAL_CHAIN" ) ) );
184}
185
186
187// Reject an attempt to claim a net already owned by a committed chain. Without this
188// guard, GetNetChainForNet() (which returns the first match) silently picks one chain
189// over the other based on iteration order, producing ambiguous netclass / colour
190// resolution.
191BOOST_FIXTURE_TEST_CASE( NetChain_ManualForceCreate_RejectsNetCollision, NETCHAIN_MANUAL_FIXTURE )
192{
194 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ), m_schematic );
195
196 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
197 BOOST_REQUIRE( graph );
198
199 graph->Recalculate( m_schematic->BuildSheetListSortedByPageNumbers(), true );
200
201 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
202
203 SCH_SYMBOL* symA = nullptr;
204 SCH_SYMBOL* symB = nullptr;
205 SCH_PIN* pinA = nullptr;
206 SCH_PIN* pinB = nullptr;
207 wxString refA, refB, pinNumA, pinNumB;
208 wxString netA, netB;
209
210 for( const SCH_SHEET_PATH& sp : sheets )
211 {
212 SCH_SCREEN* sc = sp.LastScreen();
213
214 if( !sc )
215 continue;
216
217 for( SCH_ITEM* item : sc->Items().OfType( SCH_SYMBOL_T ) )
218 {
219 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
220
221 for( SCH_PIN* p : sym->GetPins( &sp ) )
222 {
223 if( !p->Connection() || p->Connection()->Name().IsEmpty() )
224 continue;
225
226 if( !symA )
227 {
228 symA = sym;
229 pinA = p;
230 refA = sym->GetRef( &sp );
231 pinNumA = p->GetNumber();
232 netA = p->Connection()->Name();
233 }
234 else if( sym != symA && p->Connection()->Name() != netA )
235 {
236 symB = sym;
237 pinB = p;
238 refB = sym->GetRef( &sp );
239 pinNumB = p->GetNumber();
240 netB = p->Connection()->Name();
241 break;
242 }
243 }
244
245 if( symB )
246 break;
247 }
248
249 if( symB )
250 break;
251 }
252
253 BOOST_REQUIRE( symA && symB && pinA && pinB );
254
255 std::set<SCH_SYMBOL*> symbols { symA, symB };
256 std::set<wxString> nets { netA, netB };
257
258 SCH_NETCHAIN* first = graph->CreateManualNetChain(
259 wxT( "FIRST" ), symbols, nets, pinA->m_Uuid, pinB->m_Uuid, refA, pinNumA, refB, pinNumB );
260 BOOST_REQUIRE( first );
261
262 // Attempt a second chain with overlapping membership. Must be rejected.
263 std::set<wxString> overlapNets { netA };
264 SCH_NETCHAIN* collision = graph->CreateManualNetChain(
265 wxT( "SECOND" ), symbols, overlapNets, pinA->m_Uuid, pinB->m_Uuid, refA, pinNumA, refB,
266 pinNumB );
267
268 BOOST_CHECK( collision == nullptr );
269 BOOST_CHECK( graph->GetNetChainByName( wxT( "SECOND" ) ) == nullptr );
270
271 // The first chain still owns the contested net.
272 BOOST_CHECK( graph->GetNetChainForNet( netA ) == first );
273}
274
275
276// A manual chain has no underlying inferred potential, so the existing terminal-ref
277// restore path can't reconstruct it. Verify that the (nets ...) sexpr field combined
278// with the manual fallback in RebuildNetChains restores it after a save->reload cycle.
279BOOST_FIXTURE_TEST_CASE( NetChain_ManualForceCreate_SurvivesSaveReload, NETCHAIN_MANUAL_FIXTURE )
280{
282 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ), m_schematic );
283
284 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
285 BOOST_REQUIRE( graph );
286
287 graph->Recalculate( m_schematic->BuildSheetListSortedByPageNumbers(), true );
288
289 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
290 BOOST_REQUIRE( !sheets.empty() );
291
292 SCH_SYMBOL* symA = nullptr;
293 SCH_SYMBOL* symB = nullptr;
294 SCH_PIN* pinA = nullptr;
295 SCH_PIN* pinB = nullptr;
296 wxString refA, refB, pinNumA, pinNumB;
297 wxString netA, netB;
298
299 for( const SCH_SHEET_PATH& sp : sheets )
300 {
301 SCH_SCREEN* sc = sp.LastScreen();
302
303 if( !sc )
304 continue;
305
306 for( SCH_ITEM* item : sc->Items().OfType( SCH_SYMBOL_T ) )
307 {
308 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
309
310 for( SCH_PIN* p : sym->GetPins( &sp ) )
311 {
312 if( !p->Connection() || p->Connection()->Name().IsEmpty() )
313 continue;
314
315 if( !symA )
316 {
317 symA = sym;
318 pinA = p;
319 refA = sym->GetRef( &sp );
320 pinNumA = p->GetNumber();
321 netA = p->Connection()->Name();
322 }
323 else if( sym != symA && p->Connection()->Name() != netA )
324 {
325 symB = sym;
326 pinB = p;
327 refB = sym->GetRef( &sp );
328 pinNumB = p->GetNumber();
329 netB = p->Connection()->Name();
330 break;
331 }
332 }
333
334 if( symB )
335 break;
336 }
337
338 if( symB )
339 break;
340 }
341
342 BOOST_REQUIRE( symA && symB && pinA && pinB );
343
344 std::set<SCH_SYMBOL*> symbols { symA, symB };
345 std::set<wxString> nets { netA, netB };
346
347 SCH_NETCHAIN* committed = graph->CreateManualNetChain(
348 wxT( "ROUNDTRIP" ), symbols, nets, pinA->m_Uuid, pinB->m_Uuid, refA, pinNumA, refB,
349 pinNumB );
350 BOOST_REQUIRE( committed );
351 committed->SetNetClass( wxT( "rt_class" ) );
352
353 // Round-trip via the kicad_sexpr IO.
354 wxString tmpDir = wxFileName::CreateTempFileName( wxT( "kicad_qa_netchain_" ) );
355 wxRemoveFile( tmpDir );
356 wxMkdir( tmpDir );
357 wxString tmpFile = tmpDir + wxFileName::GetPathSeparator() + wxT( "out.kicad_sch" );
358
359 SCH_SHEET* root = m_schematic->GetTopLevelSheet( 0 );
360 BOOST_REQUIRE( root );
361
362 KI_TEST::DumpSchematicToFile( *m_schematic, *root, tmpFile.ToStdString() );
363
364 // Reload from the dumped file. ReadSchematicFromStream pipes the net-chain
365 // override maps into the new connection graph for us.
366 std::ifstream stream( tmpFile.ToStdString() );
367 BOOST_REQUIRE( stream.good() );
368
369 PROJECT* prj = &m_schematic->Project();
370 std::unique_ptr<SCHEMATIC> reloaded = KI_TEST::ReadSchematicFromStream( stream, prj );
371 BOOST_REQUIRE( reloaded );
372
373 reloaded->ConnectionGraph()->Recalculate( reloaded->BuildSheetListSortedByPageNumbers(), true );
374
375 SCH_NETCHAIN* restored = reloaded->ConnectionGraph()->GetNetChainByName( wxT( "ROUNDTRIP" ) );
376 BOOST_REQUIRE_MESSAGE( restored, "Manual net chain failed to survive save/reload" );
377
378 BOOST_CHECK_EQUAL( restored->GetName(), wxString( wxT( "ROUNDTRIP" ) ) );
379 BOOST_CHECK_EQUAL( restored->GetNetClass(), wxString( wxT( "rt_class" ) ) );
380 BOOST_CHECK_EQUAL( restored->GetNets().count( netA ), 1u );
381 BOOST_CHECK_EQUAL( restored->GetNets().count( netB ), 1u );
382
383 wxRemoveFile( tmpFile );
384 wxRmdir( tmpDir );
385}
386
387
388// Exercises the panel rename path against a manual chain. Member-net overrides must
389// be rekeyed alongside the netclass / colour / terminal-ref maps; without it, a
390// subsequent Recalculate would either lose the manual chain (override missing under
391// the new name) or resurrect a stale ghost under the old name.
392BOOST_FIXTURE_TEST_CASE( NetChain_ManualForceCreate_RenameRekeysMemberNetOverrides,
394{
396 KI_TEST::LoadSchematic( m_settingsManager, wxString( "net_chains_four_nets" ), m_schematic );
397
398 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
399 BOOST_REQUIRE( graph );
400
401 graph->Recalculate( m_schematic->BuildSheetListSortedByPageNumbers(), true );
402
403 SCH_SHEET_LIST sheets = m_schematic->BuildSheetListSortedByPageNumbers();
404 BOOST_REQUIRE( !sheets.empty() );
405
406 SCH_SYMBOL* symA = nullptr;
407 SCH_SYMBOL* symB = nullptr;
408 SCH_PIN* pinA = nullptr;
409 SCH_PIN* pinB = nullptr;
410 wxString refA, refB, pinNumA, pinNumB;
411 wxString netA, netB;
412
413 for( const SCH_SHEET_PATH& sp : sheets )
414 {
415 SCH_SCREEN* sc = sp.LastScreen();
416
417 if( !sc )
418 continue;
419
420 for( SCH_ITEM* item : sc->Items().OfType( SCH_SYMBOL_T ) )
421 {
422 SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( item );
423
424 for( SCH_PIN* p : sym->GetPins( &sp ) )
425 {
426 if( !p->Connection() || p->Connection()->Name().IsEmpty() )
427 continue;
428
429 if( !symA )
430 {
431 symA = sym;
432 pinA = p;
433 refA = sym->GetRef( &sp );
434 pinNumA = p->GetNumber();
435 netA = p->Connection()->Name();
436 }
437 else if( sym != symA && p->Connection()->Name() != netA )
438 {
439 symB = sym;
440 pinB = p;
441 refB = sym->GetRef( &sp );
442 pinNumB = p->GetNumber();
443 netB = p->Connection()->Name();
444 break;
445 }
446 }
447
448 if( symB )
449 break;
450 }
451
452 if( symB )
453 break;
454 }
455
456 BOOST_REQUIRE( symA && symB && pinA && pinB );
457
458 std::set<SCH_SYMBOL*> symbols { symA, symB };
459 std::set<wxString> nets { netA, netB };
460
461 // Seed the member-net override map under the original name, exactly as the
462 // sexpr parser would after reading a (nets ...) field.
463 std::map<wxString, std::set<wxString>> seed;
464 seed[wxT( "ORIG" )] = nets;
466
467 SCH_NETCHAIN* committed = graph->CreateManualNetChain(
468 wxT( "ORIG" ), symbols, nets, pinA->m_Uuid, pinB->m_Uuid, refA, pinNumA, refB,
469 pinNumB );
470 BOOST_REQUIRE( committed );
471
472 // Rename through the same API the panel uses on Apply.
473 BOOST_REQUIRE( graph->RenameCommittedNetChain( wxT( "ORIG" ), wxT( "NEW_NAME" ) ) );
474
475 const auto& memberOverrides = graph->GetNetChainMemberNetOverrides();
476 BOOST_CHECK_EQUAL( memberOverrides.count( wxT( "ORIG" ) ), 0u );
477 BOOST_CHECK_EQUAL( memberOverrides.count( wxT( "NEW_NAME" ) ), 1u );
478
479 auto it = memberOverrides.find( wxT( "NEW_NAME" ) );
480 BOOST_REQUIRE( it != memberOverrides.end() );
481 BOOST_CHECK_EQUAL( it->second.count( netA ), 1u );
482 BOOST_CHECK_EQUAL( it->second.count( netB ), 1u );
483
484 BOOST_CHECK( graph->GetNetChainByName( wxT( "ORIG" ) ) == nullptr );
485 BOOST_CHECK( graph->GetNetChainByName( wxT( "NEW_NAME" ) ) == committed );
486
487 // DeleteCommittedNetChain must drop the rekeyed override entry along with the chain.
488 BOOST_REQUIRE( graph->DeleteCommittedNetChain( wxT( "NEW_NAME" ) ) );
489 BOOST_CHECK_EQUAL( graph->GetNetChainMemberNetOverrides().count( wxT( "NEW_NAME" ) ), 0u );
490}
Calculate the connectivity of a schematic and generates netlists.
SCH_NETCHAIN * GetNetChainByName(const wxString &aName)
void SetNetChainMemberNetOverrides(const std::map< wxString, std::set< wxString > > &aOverrides)
Stash per-chain member-net lists read from the schematic file.
const std::map< wxString, std::set< wxString > > & GetNetChainMemberNetOverrides() const
SCH_NETCHAIN * GetNetChainForNet(const wxString &aNet)
bool RenameCommittedNetChain(const wxString &aOld, const wxString &aNew)
Rename a committed net chain.
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Update the connection graph for the given list of sheets.
SCH_NETCHAIN * CreateManualNetChain(const wxString &aName, const std::set< class SCH_SYMBOL * > &aSymbols, const std::set< wxString > &aNets, const KIID &aTerminalPinA, const KIID &aTerminalPinB, const wxString &aRefA, const wxString &aPinNumA, const wxString &aRefB, const wxString &aPinNumB)
Commit a manually-defined net chain that the inferred-potential pass did not produce.
const std::vector< std::unique_ptr< SCH_NETCHAIN > > & GetCommittedNetChains() const
Return user-created (committed) net chains (legacy accessor retained under net-chain API).
bool DeleteCommittedNetChain(const wxString &aName)
Delete a committed net chain by name.
const KIID m_Uuid
Definition eda_item.h:528
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:225
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:41
Container for project specific data.
Definition project.h:66
wxString Name(bool aIgnoreSheet=false) const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition sch_item.cpp:491
A net chain is a collection of nets that are connected together through passive components.
const std::set< wxString > & GetNets() const
const wxString & GetTerminalRef(int aIdx) const
const wxString & GetNetClass() const
const wxString & GetName() const
const std::set< class SCH_SYMBOL * > & GetSymbols() const
void SetNetClass(const wxString &aNetClass)
Net chains may override the netclass applied to every member net.
const wxString & GetTerminalPinNum(int aIdx) const
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:119
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
Schematic symbol object.
Definition sch_symbol.h:76
const wxString & GetNetChainName() const
Definition sch_symbol.h:862
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
std::unique_ptr< SCHEMATIC > ReadSchematicFromStream(std::istream &aStream, PROJECT *aProject)
void LoadSchematic(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< SCHEMATIC > &aSchematic)
void DumpSchematicToFile(SCHEMATIC &aSchematic, SCH_SHEET &aSheet, const std::string &aFilename)
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< FAB_LAYER_COLOR > dummy
std::unique_ptr< SCHEMATIC > m_schematic
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_FIXTURE_TEST_CASE(NetChain_ManualForceCreate_CommitsAndQueryable, NETCHAIN_MANUAL_FIXTURE)
const uint32_t seed
const SHAPE_LINE_CHAIN chain
BOOST_CHECK_EQUAL(result, "25.4")
@ SCH_SYMBOL_T
Definition typeinfo.h:173