investigate insidious ill-guided conversion

As it turns out, using the functional-notation form conversion
with *parentheses* will fall back on a C-style (wild, re-interpret) cast
when the target type is *not* a class. As in the case in question here, where
it is a const& to a class. To the contrary, using *curly braces* will always
attempt to go through a constructor, and thus fail as expected, when there is
no conversion path available.

I wasn't aware of that pitfall. I noticed it since the recently introduced
class TimelineGui lacked a conversion operator to BareEntryID const& and just
happily used the TimelineGui object itself and did a reinterpret_cast into BareEntryID
This commit is contained in:
Fischlurch 2018-10-12 23:42:56 +02:00
parent e81b0592d3
commit fa6ba76f85
14 changed files with 92 additions and 69 deletions

View file

@ -39,11 +39,14 @@
// 03/18 - Dependency Injection / Singleton initialisation / double checked locking // 03/18 - Dependency Injection / Singleton initialisation / double checked locking
// 04/18 - investigate construction of static template members // 04/18 - investigate construction of static template members
// 08/18 - Segfault when compiling some regular expressions for EventLog search // 08/18 - Segfault when compiling some regular expressions for EventLog search
// 10/18 - investigate insidious reinterpret cast
/** @file try.cpp /** @file try.cpp
* Heisenbug hunt: random crashes in BusTerm_test, seemingly emerging from regular expression compilation. * Document an insidious wild cast, caused by the syntax `Type(arg)`.
* Not able to reproduce the crash, unfortunately. //////////////////////////////////////////////////////////TICKET #1158 * I was under the wrong assumption this would be handled equivalent to a constructor invocation.
* Seemingly it is rather handled as a C-style cast, i.e. equivalent to `(Type)arg`.
* @see [Question on Stackoverflow](https://stackoverflow.com/q/52782967/444796)
*/ */
typedef unsigned int uint; typedef unsigned int uint;
@ -52,12 +55,10 @@ typedef unsigned int uint;
#include "lib/test/test-helper.hpp" #include "lib/test/test-helper.hpp"
#include "lib/util.hpp" #include "lib/util.hpp"
#include <regex>
#include <string> #include <string>
#include <vector>
using std::string; using std::string;
using VecS = std::vector<string>; using util::isSameObject;
@ -69,25 +70,32 @@ using VecS = std::vector<string>;
class Wau
{
int i = -1;
};
class Miau
{
public:
uint u = 1;
};
int int
main (int, char**) main (int, char**)
{ {
VecS rexs{{"after.+_ATTRIBS_.+ins.+1 of 62 ≺293.gen029≻.+mut.+1 of 62 ≺293.gen029≻.+ins.+borgID.+293.+emu.+1 of 62 ≺293.gen029≻" Wau wau;
,"after.+_ATTRIBS_.+ins.+1 of 64 ≺251.gen019≻.+mut.+1 of 64 ≺251.gen019≻.+ins.+borgID.+251.+emu.+1 of 64 ≺251.gen019≻" using ID = Miau &;
,"after.+_ATTRIBS_.+ins.+1 of 8 ≺203.gen036≻.+mut.+1 of 8 ≺203.gen036≻.+ins.+borgID.+203.+emu.+1 of 8 ≺203.gen036≻" ID wuff = ID(wau);
,"after.+?_ATTRIBS_.+?ins.+?53 of 57 ≺358.gen010≻.+?mut.+?53 of 57 ≺358.gen010≻.+?ins.+?borgID.+?358.+?emu.+?53 of 57 ≺358.gen010≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 63 ≺178.gen028≻.+?mut.+?53 of 63 ≺178.gen028≻.+?ins.+?borgID.+?178.+?emu.+?53 of 63 ≺178.gen028≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 59 ≺498.gen038≻.+?mut.+?53 of 59 ≺498.gen038≻.+?ins.+?borgID.+?498.+?emu.+?53 of 59 ≺498.gen038≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 60 ≺223.gen003≻.+?mut.+?53 of 60 ≺223.gen003≻.+?ins.+?borgID.+?223.+?emu.+?53 of 60 ≺223.gen003≻"
,"after.+?_ATTRIBS_.+?ins.+?53 of 78 ≺121.gen015≻.+?mut.+?53 of 78 ≺121.gen015≻.+?ins.+?borgID.+?121.+?emu.+?53 of 78 ≺121.gen015≻"
}};
for (auto const& str : rexs)
{
auto rex = std::regex{str};
SHOW_EXPR (std::regex_search(str, rex));
}
cout << "Miau=" << wuff.u
<< " ref to same object: " << isSameObject (wau, wuff)
<< endl;
cout << "\n.gulp.\n"; cout << "\n.gulp.\n";

View file

@ -140,7 +140,7 @@ namespace interact {
}) })
.matchElement ([&](GenNode const& spec, TimelineGui const& elm) -> bool .matchElement ([&](GenNode const& spec, TimelineGui const& elm) -> bool
{ // »Matcher« : how to know we're dealing with the right timeline object { // »Matcher« : how to know we're dealing with the right timeline object
return spec.idi == ID(elm); return spec.idi == ID{elm};
}) })
.constructFrom ([&](GenNode const& spec) -> TimelineGui .constructFrom ([&](GenNode const& spec) -> TimelineGui
{ // »Constructor« : what to do when the diff mentions a new entity { // »Constructor« : what to do when the diff mentions a new entity
@ -148,7 +148,7 @@ namespace interact {
}) })
.buildChildMutator ([&](TimelineGui& targetTimeline, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool .buildChildMutator ([&](TimelineGui& targetTimeline, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ // »Mutator« : how to apply the diff recursively to a nested scope { // »Mutator« : how to apply the diff recursively to a nested scope
if (ID(targetTimeline) != subID) return false; if (ID{targetTimeline} != subID) return false;
targetTimeline.buildMutator (buff); // - delegate to child(Timeline) to build nested TreeMutator targetTimeline.buildMutator (buff); // - delegate to child(Timeline) to build nested TreeMutator
return true; return true;
})) }))

View file

@ -99,15 +99,15 @@ namespace timeline {
}) })
.matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool .matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool
{ {
return spec.idi == ID(elm); return spec.idi == ID{*elm};
}) })
.constructFrom ([&](GenNode const& spec) -> PMarker .constructFrom ([&](GenNode const& spec) -> PMarker
{ {
return make_unique<MarkerWidget>(spec.idi, this->uiBus_); return make_unique<MarkerWidget> (spec.idi, this->uiBus_);
}) })
.buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool .buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ {
if (ID(target) != subID) return false; if (ID{*target} != subID) return false;
target->buildMutator (buff); target->buildMutator (buff);
return true; return true;
})) }))
@ -118,15 +118,15 @@ namespace timeline {
}) })
.matchElement ([&](GenNode const& spec, PEffect const& elm) -> bool .matchElement ([&](GenNode const& spec, PEffect const& elm) -> bool
{ {
return spec.idi == ID(elm); return spec.idi == ID{*elm};
}) })
.constructFrom ([&](GenNode const& spec) -> PEffect .constructFrom ([&](GenNode const& spec) -> PEffect
{ {
return make_unique<ClipPresenter>(spec.idi, this->uiBus_); return make_unique<ClipPresenter> (spec.idi, this->uiBus_);
}) })
.buildChildMutator ([&](PEffect& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool .buildChildMutator ([&](PEffect& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ {
if (ID(target) != subID) return false; if (ID{*target} != subID) return false;
target->buildMutator (buff); target->buildMutator (buff);
return true; return true;
})) }))
@ -137,15 +137,15 @@ namespace timeline {
}) })
.matchElement ([&](GenNode const& spec, PChannel const& elm) -> bool .matchElement ([&](GenNode const& spec, PChannel const& elm) -> bool
{ {
return spec.idi == ID(elm); return spec.idi == ID{*elm};
}) })
.constructFrom ([&](GenNode const& spec) -> PChannel .constructFrom ([&](GenNode const& spec) -> PChannel
{ {
return make_unique<ClipPresenter>(spec.idi, this->uiBus_); return make_unique<ClipPresenter> (spec.idi, this->uiBus_);
}) })
.buildChildMutator ([&](PChannel& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool .buildChildMutator ([&](PChannel& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ {
if (ID(target) != subID) return false; if (ID{*target} != subID) return false;
target->buildMutator (buff); target->buildMutator (buff);
return true; return true;
}))); })));

View file

@ -144,7 +144,7 @@ namespace timeline {
}) })
.matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool .matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool
{ // »Matcher« : how to know we're dealing with the right object { // »Matcher« : how to know we're dealing with the right object
return spec.idi == ID(elm); return spec.idi == ID{*elm};
}) })
.constructFrom ([&](GenNode const& spec) -> PMarker .constructFrom ([&](GenNode const& spec) -> PMarker
{ // »Constructor« : what to do when the diff mentions a new entity { // »Constructor« : what to do when the diff mentions a new entity
@ -152,7 +152,7 @@ namespace timeline {
}) })
.buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool .buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ // »Mutator« : how to apply the diff recursively to a nested scope { // »Mutator« : how to apply the diff recursively to a nested scope
if (ID(target) != subID) return false; // - require match on already existing child object if (ID{*target} != subID) return false; // - require match on already existing child object
target->buildMutator (buff); // - delegate to child to build nested TreeMutator target->buildMutator (buff); // - delegate to child to build nested TreeMutator
return true; return true;
})) }))

View file

@ -56,6 +56,8 @@ namespace timeline {
, rootTrackID_{trackID} , rootTrackID_{trackID}
{ } { }
TimelineGui::~TimelineGui() { }
/** /**
* actually build a TimelineWidget to enact the role * actually build a TimelineWidget to enact the role

View file

@ -81,8 +81,12 @@ namespace timeline {
public: public:
TimelineGui (ID identity, ID trackID); TimelineGui (ID identity, ID trackID);
virtual ~TimelineGui();
// standard copy operations aceptable // standard copy operations acceptable
operator ID() const { return timelineID_; }
/** actually build a TimelineWidget to enact the role /** actually build a TimelineWidget to enact the role

View file

@ -109,15 +109,15 @@ namespace timeline {
}) })
.matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool .matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool
{ {
return spec.idi == ID(elm); return spec.idi == ID{*elm};
}) })
.constructFrom ([&](GenNode const& spec) -> PMarker .constructFrom ([&](GenNode const& spec) -> PMarker
{ {
return make_unique<MarkerWidget>(spec.idi, this->uiBus_); return make_unique<MarkerWidget> (spec.idi, this->uiBus_);
}) })
.buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool .buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ {
if (ID(target) != subID) return false; if (ID{*target} != subID) return false;
target->buildMutator (buff); target->buildMutator (buff);
return true; return true;
})) }))
@ -128,15 +128,15 @@ namespace timeline {
}) })
.matchElement ([&](GenNode const& spec, PClip const& elm) -> bool .matchElement ([&](GenNode const& spec, PClip const& elm) -> bool
{ {
return spec.idi == ID(elm); return spec.idi == ID{*elm};
}) })
.constructFrom ([&](GenNode const& spec) -> PClip .constructFrom ([&](GenNode const& spec) -> PClip
{ {
return make_unique<ClipPresenter>(spec.idi, this->uiBus_); return make_unique<ClipPresenter> (spec.idi, this->uiBus_);
}) })
.buildChildMutator ([&](PClip& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool .buildChildMutator ([&](PClip& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ {
if (ID(target) != subID) return false; if (ID{*target} != subID) return false;
target->buildMutator (buff); target->buildMutator (buff);
return true; return true;
})) }))
@ -147,15 +147,15 @@ namespace timeline {
}) })
.matchElement ([&](GenNode const& spec, PFork const& elm) -> bool .matchElement ([&](GenNode const& spec, PFork const& elm) -> bool
{ {
return spec.idi == ID(elm); return spec.idi == ID{*elm};
}) })
.constructFrom ([&](GenNode const& spec) -> PFork .constructFrom ([&](GenNode const& spec) -> PFork
{ {
return make_unique<TrackPresenter>(spec.idi, this->uiBus_); return make_unique<TrackPresenter> (spec.idi, this->uiBus_);
}) })
.buildChildMutator ([&](PFork& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool .buildChildMutator ([&](PFork& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ {
if (ID(target) != subID) return false; if (ID{*target} != subID) return false;
target->buildMutator (buff); target->buildMutator (buff);
return true; return true;
}))); })));

View file

@ -224,6 +224,7 @@ namespace diff{
{ } { }
public: public:
explicit
ID (GenNode const& node) ID (GenNode const& node)
: ID(node.idi) : ID(node.idi)
{ } { }

View file

@ -147,6 +147,13 @@ namespace test{
static_assert (not sizeof(X), "### Type Debugging ###"); static_assert (not sizeof(X), "### Type Debugging ###");
}; };
template<typename X>
void
typeDebugger(X&& x)
{
static_assert (not sizeof(X), "### Type Debugging ###");
}
namespace { // helper for printing type diagnostics namespace { // helper for printing type diagnostics

View file

@ -347,7 +347,7 @@ namespace asset {
/** marker constant denoting a NIL asset */ /** marker constant denoting a NIL asset */
template<class KIND> template<class KIND>
ID<KIND> ID<KIND>::INVALID = ID(0); ID<KIND> ID<KIND>::INVALID = ID{0};
}} // namespace proc::asset }} // namespace proc::asset

View file

@ -75,7 +75,7 @@ namespace proc {
class ImplFacade; class ImplFacade;
class ImplConstraint; class ImplConstraint;
typedef lib::idi::EntryID<StreamType> ID; using ID = lib::idi::EntryID<StreamType>;
Prototype const& prototype; Prototype const& prototype;

View file

@ -64,9 +64,10 @@ namespace test {
*/ */
class BasicPipe_test : public Test class BasicPipe_test : public Test
{ {
virtual void run(Arg arg) virtual void
run (Arg arg)
{ {
string pipeID = isnil(arg)? "Black Hole" : arg[1]; string pipeID = isnil (arg)? "Black Hole" : arg[1];
string streamID = 2>arg.size()? "teststream" : arg[2] ; string streamID = 2>arg.size()? "teststream" : arg[2] ;
createExplicit (pipeID,streamID); createExplicit (pipeID,streamID);
@ -80,7 +81,7 @@ namespace test {
void createExplicit (string pID, string sID) void createExplicit (string pID, string sID)
{ {
string pID_sane (pID); string pID_sane{pID};
normaliseID (pID_sane); normaliseID (pID_sane);
CHECK (pID_sane != pID); CHECK (pID_sane != pID);
@ -89,7 +90,7 @@ namespace test {
CHECK (thePipe); CHECK (thePipe);
CHECK (thePipe->getProcPatt()); CHECK (thePipe->getProcPatt());
CHECK (thePipe->getPipeID() == pID_sane); CHECK (thePipe->getPipeID() == pID_sane);
CHECK (thePipe->getStreamID() == StreamType::ID(sID)); CHECK (thePipe->getStreamID() == StreamType::ID{sID});
CHECK (thePipe->shortDesc == pID_sane); CHECK (thePipe->shortDesc == pID_sane);
Asset::Ident idi = thePipe->ident; Asset::Ident idi = thePipe->ident;
@ -97,9 +98,9 @@ namespace test {
CHECK (contains (idi.name, thePipe->getPipeID())); CHECK (contains (idi.name, thePipe->getPipeID()));
CHECK (contains (idi.name, thePipe->getStreamID())); CHECK (contains (idi.name, thePipe->getStreamID()));
Category cat (idi.category); Category cat{idi.category};
Category refcat (STRUCT,"pipes"); Category refcat{STRUCT,"pipes"};
CHECK ( cat.hasKind(STRUCT) ); CHECK ( cat.hasKind (STRUCT) );
CHECK ( cat.isWithin(refcat) ); CHECK ( cat.isWithin(refcat) );
} }
@ -131,25 +132,25 @@ namespace test {
PPipe pipe1 = Pipe::query (""); // "the default pipe" PPipe pipe1 = Pipe::query (""); // "the default pipe"
PPipe pipe2; PPipe pipe2;
CHECK (pipe1); CHECK (pipe1);
CHECK (pipe1 == Session::current->defaults (Query<Pipe>())); CHECK (pipe1 == Session::current->defaults (Query<Pipe>{}));
CHECK (pipe1->ident.category.hasKind(VIDEO)); CHECK (pipe1->ident.category.hasKind(VIDEO));
CHECK (pipe1->getProcPatt()); CHECK (pipe1->getProcPatt());
PProcPatt propa = Session::current->defaults (Query<const ProcPatt>("pipe(default)")); PProcPatt propa = Session::current->defaults (Query<const ProcPatt>{"pipe(default)"});
CHECK (propa == pipe1->getProcPatt()); CHECK (propa == pipe1->getProcPatt());
// several variants to query for "the default pipe" // several variants to query for "the default pipe"
pipe2 = Session::current->defaults(Query<Pipe> ()); pipe2 = Session::current->defaults(Query<Pipe>{});
CHECK (pipe2 == pipe1); CHECK (pipe2 == pipe1);
pipe2 = asset::Struct::retrieve (Query<Pipe> ()); pipe2 = asset::Struct::retrieve (Query<Pipe>{});
CHECK (pipe2 == pipe1); CHECK (pipe2 == pipe1);
pipe2 = asset::Struct::retrieve (Query<Pipe> ("pipe(default)")); pipe2 = asset::Struct::retrieve (Query<Pipe>{"pipe(default)"});
CHECK (pipe2 == pipe1); CHECK (pipe2 == pipe1);
string sID = pipe1->getStreamID(); // sort of a "default stream type" string sID = pipe1->getStreamID(); // sort of a "default stream type"
PPipe pipe3 = Pipe::query ("stream("+sID+")"); PPipe pipe3 = Pipe::query ("stream("+sID+")");
CHECK (pipe3); CHECK (pipe3);
CHECK (pipe3->getStreamID() == StreamType::ID(sID)); CHECK (pipe3->getStreamID() == StreamType::ID{sID});
CHECK (pipe3->getProcPatt() == Session::current->defaults (Query<const ProcPatt>("stream("+sID+")"))); CHECK (pipe3->getProcPatt() == Session::current->defaults (Query<const ProcPatt>{"stream("+sID+")"}));
} }
@ -174,7 +175,7 @@ namespace test {
// default pipe for this pattern automatically // default pipe for this pattern automatically
PPipe pipe2x = Pipe::query ("pattern(another)"); PPipe pipe2x = Pipe::query ("pattern(another)");
CHECK (pattern2 == pipe2x->getProcPatt()); CHECK (pattern2 == pipe2x->getProcPatt());
CHECK (pipe2x == Session::current->defaults (Query<Pipe>("pattern(another)"))); CHECK (pipe2x == Session::current->defaults (Query<Pipe>{"pattern(another)"}));
thePipe->switchProcPatt(pattern2); thePipe->switchProcPatt(pattern2);
CHECK ( dependencyCheck (thePipe, pattern2)); CHECK ( dependencyCheck (thePipe, pattern2));
@ -197,7 +198,7 @@ namespace test {
PPipe pipe3x = Pipe::query ("pattern(another)"); PPipe pipe3x = Pipe::query ("pattern(another)");
pattern3 = pipe3x->getProcPatt(); /////TODO: transition to P<> pattern3 = pipe3x->getProcPatt(); /////TODO: transition to P<>
CHECK (pattern3 != pattern2); // because pattern2 is already unlinked... CHECK (pattern3 != pattern2); // because pattern2 is already unlinked...
CHECK (pipe3x == Session::current->defaults (Query<Pipe>("pattern(another)"))); CHECK (pipe3x == Session::current->defaults (Query<Pipe>{"pattern(another)"}));
CHECK (pipe3x != pipe2x); // ..we got a new default pipe for "pattern(another)" too! CHECK (pipe3x != pipe2x); // ..we got a new default pipe for "pattern(another)" too!

View file

@ -128,16 +128,16 @@ namespace test {
retrieveConstrainedDefault (string pID, string sID) retrieveConstrainedDefault (string pID, string sID)
{ {
PPipe pipe1 = Pipe::query (""); // "the default pipe" PPipe pipe1 = Pipe::query (""); // "the default pipe"
CHECK ( pipe1->getStreamID() != StreamType::ID(sID), CHECK ( pipe1->getStreamID() != StreamType::ID{sID},
"stream-ID \"%s\" not suitable for test, because " "stream-ID \"%s\" not suitable for test, because "
"the default-pipe \"%s\" happens to have the same " "the default-pipe \"%s\" happens to have the same "
"stream-ID. We need it to be different", "stream-ID. We need it to be different",
sID.c_str(), pID.c_str() sID.c_str(), pID.c_str()
); );
string query_for_sID ("stream("+sID+")"); string query_for_sID{"stream("+sID+")"};
PPipe pipe2 = Pipe::query (query_for_sID); PPipe pipe2 = Pipe::query (query_for_sID);
CHECK (pipe2->getStreamID() == StreamType::ID(sID)); CHECK (pipe2->getStreamID() == StreamType::ID{sID});
CHECK (pipe2 != pipe1); CHECK (pipe2 != pipe1);
CHECK (pipe2 == Pipe::query (query_for_sID)); // reproducible CHECK (pipe2 == Pipe::query (query_for_sID)); // reproducible
} }
@ -146,13 +146,13 @@ namespace test {
void void
failureCreatesNewDefault() failureCreatesNewDefault()
{ {
PPipe pipe1 = Session::current->defaults(Query<Pipe> ("")); // "the default pipe" PPipe pipe1 = Session::current->defaults(Query<Pipe>{""}); // "the default pipe"
string new_pID = _Fmt{"dummy_%s_%i"} string new_pID = _Fmt{"dummy_%s_%i"}
% pipe1->getPipeID() % pipe1->getPipeID()
% std::rand() % std::rand()
; // make random new pipeID ; // make random new pipeID
Query<Pipe> query_for_new ("pipe("+new_pID+")"); Query<Pipe> query_for_new{"pipe("+new_pID+")"};
CHECK (!find (query_for_new)); // check it doesn't exist CHECK (!find (query_for_new)); // check it doesn't exist
PPipe pipe2 = Session::current->defaults (query_for_new); // triggers creation PPipe pipe2 = Session::current->defaults (query_for_new); // triggers creation
@ -170,13 +170,13 @@ namespace test {
void void
verifyRemoval() verifyRemoval()
{ {
Symbol pID ("some_pipe"); Symbol pID{"some_pipe"};
Query<Pipe> query_for_pID ("pipe("+pID+")"); Query<Pipe> query_for_pID{"pipe("+pID+")"};
size_t hash; size_t hash;
{ {
// create new pipe and declare it to be a default // create new pipe and declare it to be a default
PPipe pipe1 = Struct::retrieve.newInstance<Pipe> (pID); PPipe pipe1 = Struct::retrieve.newInstance<Pipe> (pID);
Session::current->defaults.define(pipe1); Session::current->defaults.define (pipe1);
CHECK (2 == pipe1.use_count()); // the pipe1 smart-ptr and the AssetManager CHECK (2 == pipe1.use_count()); // the pipe1 smart-ptr and the AssetManager
hash = pipe1->getID(); hash = pipe1->getID();

View file

@ -46,7 +46,7 @@ namespace test {
print_clean ("a Sentence"); print_clean ("a Sentence");
print_clean ("trailing Withespace\n \t"); print_clean ("trailing Withespace\n \t");
print_clean ("with a \t lot\n of Whitespace"); print_clean ("with a \t lot\n of Whitespace");
print_clean ("with\"much (punctuation)[]!"); print_clean ("@with\".\'much ($punctuation)[]!");
print_clean ("§&Ω%€ leading garbage"); print_clean ("§&Ω%€ leading garbage");
print_clean ("mixed Ω garbage"); print_clean ("mixed Ω garbage");
print_clean ("Bääääh!!"); print_clean ("Bääääh!!");