C++17: some related clean-up

This commit is contained in:
Fischlurch 2020-02-21 23:55:09 +01:00
parent beb8406abe
commit 421a2ed49a
12 changed files with 34 additions and 586 deletions

View file

@ -37,7 +37,7 @@ should _not_ be done as root.
. add a suitable source line to your *Apt configuration* ('/etc/apt/sources.lst')
+
----
deb-src https://lumiera.org/debian/ jessie experimental
deb-src https://lumiera.org/debian/ buster experimental
----
. get all the *build dependencies*
+

View file

@ -40,7 +40,7 @@ Requirements
To build Lumiera, you'll need a _build environment_ together with the
_development packages_ of the libraries Lumiera depends on.
footnote:[there is a separate documentation page listing the
link:{ldoc}/technical/build/Dependencies.html[build dependencies] explicitly.
link:{ldoc}/technical/build/BuildDependencies.html[build dependencies] explicitly.
We'll try to keep that information up to date -- but in the end, the really
authoritative information about the build dependencies is encoded into the
link:{ldoc}/technical/build/SCons.html[build system]. Thus, when the build

View file

@ -31,13 +31,6 @@
** be accessed from an enclosed scope as a per-thread stack. DiagnosticContext
** provides an controlled environment for adding diagnostic code on demand; typically
** to be configured such as to resolve into an empty class for release builds.
**
** As of 2/10, this is an experimental feature in evaluation. To start with,
** I'll use it to solve the problem of providing a NoBug resource tracker handle
** without tangling the object monitor code (sync.hpp) with low level, NoBug related
** implementation details.
**
** @todo add the actual diagnostic content
**/
@ -47,9 +40,6 @@
#include "lib/error.hpp"
#include "lib/nocopy.hpp"
#include "lib/thread-local.hpp"
#include <nobug.h>
@ -58,7 +48,7 @@ namespace lib {
/**
/**
* Diagnostic data frame to collect specific information concerning a scope.
* To be placed explicitly as an automatic (stack) variable. Provides a controlled
* environment for hooking up diagnostic code. Within each thread, a stack of
@ -70,34 +60,33 @@ namespace lib {
class DiagnosticContext
: util::NonCopyable
{
typedef ThreadLocalPtr<DiagnosticContext> ThreadLocalAccess;
typedef std::vector<VAL> ValSequence;
using ValSequence = std::vector<VAL>;
const VAL value_;
DiagnosticContext * const prev_;
/** embedded thread local pointer
* to the innermost context encountered */
static ThreadLocalAccess&
static DiagnosticContext* &
current()
{
static ThreadLocalAccess accessPoint;
thread_local DiagnosticContext* accessPoint;
return accessPoint;
}
public:
DiagnosticContext(VAL const& value_to_log = VAL())
: value_(value_to_log)
, prev_(current().get())
: value_{value_to_log}
, prev_{current()}
{
current().set (this);
current() = this;
}
~DiagnosticContext()
{
ASSERT (this == current().get());
current().set (prev_);
ASSERT (this == current());
current() = prev_;
}
@ -110,7 +99,7 @@ namespace lib {
static DiagnosticContext&
access ()
{
DiagnosticContext* innermost = current().get();
DiagnosticContext* innermost = current();
if (!innermost)
throw lumiera::error::Logic ("Accessing Diagnostic context out of order; "
"an instance should have been created within "
@ -120,20 +109,17 @@ namespace lib {
/** snapshot of the current stack of diagnostic frames
* @return vector with all the payload values currently
* on the thread-local diagnostic stack. Might
* be empty. Values start with frame next to
* the current scope and end with outermost.
* @return vector with all the payload values currently on the thread-local diagnostic stack.
* Might be empty. Values start with frame next to the current scope and end with outermost.
* @warning can be inefficient on very large stacks
* @todo benchmark and improve the data structure
* used for the snapshot. Vector is not
* an optimal choice here.
* @todo benchmark and improve the data structure used for the snapshot.
* Vector is not an optimal choice here.
*/
static ValSequence
extractStack()
{
ValSequence loggedValues;
DiagnosticContext* next = current().get();
DiagnosticContext* next = current();
while (next)
{
loggedValues.push_back (*next);

View file

@ -446,8 +446,7 @@ namespace lib {
namespace iter_explorer { // Implementation of Iterator decorating layers...
/*constexpr*/ ///////////////////////////////////////////////////////////TICKET #1138 : lambdas are literal only in C++17
auto ACCEPT_ALL = [](auto){return true;};
constexpr auto ACCEPT_ALL = [](auto){return true;};
/**
* @internal technical details of binding a functor into the TreeExplorer.
@ -578,8 +577,9 @@ namespace lib {
* series of new elements. Other layers might need to sync to this operation, and thus it is passed
* down the chain. For that reason, we need a dedicated BaseAdapter to adsorb such chained calls.
* @remark when building the TreeExplorer, the to-be-wrapped source is fed down into its place
* within BaseAdapter. For that reason, we need to lift the copy ctors of the base.
* Just inheriting the base class ctors won't do that, at least not in C++14.
* within BaseAdapter. For that reason, it is not sufficient just to lift the copy ctors
* of the base (as inheriting the base class ctors would do). Rather, we need dedicated
* further copy ctors to clone and move from the _undecorated base type._
*/
template<class SRC>
struct BaseAdapter

View file

@ -1,122 +0,0 @@
/*
MAYBE.hpp - dealing with optional values
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file maybe.hpp
** Support for representation of optional values.
** This implements a concept ("option monad") known from functional programming,
** allowing to express the fact of some value possibly be unavailable. Using this
** approach allows to avoid the dangerous technique of (ab)using NULL pointers to
** represent missing values.
**
** While a NULL pointer carries this special meaning just by convention, marking a
** parameter or return value as optional states this fact first class, and enforces
** the necessary "is available" check through the type system. Surprisingly, this
** leads not only to more secure, but also much more compact code, as we're now
** able to substitute a fallback just by a "or else use this" clause.
** Basically, there are different ways to access the actual value
** - access through implicit conversion raises an exception for missing values
** - evaluation as boolean allows to check, if the value is available
** - an alternative or fallback value may be attached.
**
** @todo WIP and rather brainstorming as of 2/10
** @deprecated as of 2016 : the upcoming C++17 will provide an optional type!!
**
** @see vault::ThreadJob usage example
*/
#ifndef LIB_MAYBE_H
#define LIB_MAYBE_H
#include "lib/error.hpp"
//#include "lib/wrapper.hpp"
#include "lib/util.hpp"
#include <string>
namespace lib {
using util::isnil;
using std::string;
namespace error = lumiera::error;
namespace maybe {
}
/**
* A value, which might be unavailable
* @throw error::State on any attempt to access a missing value
* without prior checking the availability
*/
template<typename VAL>
class Maybe
{
VAL value_;
public:
/** mark an invalid/failed result */
Maybe ()
{ }
/** standard case: valid result */
Maybe (VAL const& value)
: value_(value)
{ }
bool
isValid() const
{
UNIMPLEMENTED ("check if optional value is available");
}
void
maybeThrow(Literal explanation =0) const
{
if (!isValid())
throw error::State (explanation.empty()? "optional value not available" : string(explanation),
error::LUMIERA_ERROR_BOTTOM_VALUE);
}
VAL
get() const
{
maybeThrow();
return value_;
}
};
} // namespace lib
#endif

View file

@ -47,12 +47,10 @@
**
** @remarks this is the second attempt at building a skeleton of the core factory mechanics.
** The first attempt was pre-C++11, relied on partial specialisations and was hard to
** understand and maintain. In theory, with C++11 the task should be quite simple now,
** relying on rvalue references and variadic templates. Unfortunately, as of 9/2014,
** the compiler support is not yet robust enough on Debian/stable really to deal with
** \em all the conceivable cases when forwarding arbitrary factory products. Thus
** for now we choose to avoid the "perfect forwarding" problem and rather let the
** wrapper invoke the fabrication function and handle the result properly.
** understand and maintain. Now, after C++11 the basic task was greatly simplified,
** relying on rvalue references and variadic templates. However, we still need a
** specialised factory template to allow for a _family of factory functions_ with
** common configuration.
**
** @see multifact-test.cpp
** @see multifact-singleton-test.cpp

View file

@ -1,113 +0,0 @@
/*
THREAD-LOCAL.hpp - support using thread local data
Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file thread-local.hpp
** Helpers for working with thread local data.
** As we don't want to depend on boost.threads, we'll provide some simple
** support facilities for dealing with thread local data in RAII fashion.
** Draft as of 2/10, to be extended on demand.
**
** @todo care for unit test coverage
** @todo WIP-WIP. Maybe add facilities similar to boost::specific_ptr
** @deprecated C++11 has a `thread_local` storage class...
**/
#ifndef LIB_THREAD_LOCAL_H
#define LIB_THREAD_LOCAL_H
#include "lib/error.hpp"
#include "lib/nocopy.hpp"
#include <pthread.h>
namespace lib {
/**
* Thread local pointer without ownership management.
* This (noncopyable) smart-ptr class cares for registering and
* deregistering the per-instance access key, but besides that
* behaves passively, like a normal pointer. When first accessed,
* the pointer is NIL in each new thread; it may be set by assignment.
*/
template<typename TAR>
class ThreadLocalPtr
: util::NonCopyable
{
pthread_key_t key_;
public:
ThreadLocalPtr()
{
if (pthread_key_create (&key_, NULL))
throw lumiera::error::External ("unable to create key for thread local data");
}
~ThreadLocalPtr()
{
WARN_IF (pthread_key_delete (key_), sync, "failure to drop thread-local data key");
}
explicit operator bool() const { return isValid(); }
bool isValid() const { return get(); }
TAR& operator* () const { return *accessChecked(); }
TAR* operator-> () const { return accessChecked(); }
void operator= (TAR& tar) { set(&tar); }
TAR*
get() const
{
return static_cast<TAR*> (pthread_getspecific (key_));
}
void
set (TAR* pointee)
{
if (pthread_setspecific (key_, pointee))
throw lumiera::error::External ("failed to store thread local data");
}
private:
TAR*
accessChecked() const
{
TAR *p(get());
if (!p)
throw lumiera::error::State ("dereferencing a thread local NULL pointer"
,lumiera::error::LERR_(BOTTOM_VALUE));
return p;
}
};
} // namespace lib
#endif

View file

@ -125,11 +125,6 @@ return: 0
END
TEST "Wrapper thread-local pointers" ThreadLocal_test <<END
return: 0
END
TEST "Thread-local diagnostic context" DiagnosticContext_test <<END
return: 0
END

View file

@ -188,13 +188,11 @@ namespace test{
VecI sequence = descend (seed);
uint prev = 0;
for (uint i=0; i < sequence.size(); ++i)
{
uint val = sequence[i];
if (! (isOdd(val) && seed >= val && val > prev ))
throw error::Fatal ("thread-local diagnostic stack");
for (uint val : sequence)
if (not (isOdd(val) and seed >= val and val > prev ))
throw error::Fatal ("thread-local diagnostic stack");
else
prev = val;
}
}
static VecI

View file

@ -977,6 +977,11 @@ namespace test{
+sizeof(void*) // the VTable for each layer of TreeMutator impl
)
+ 1 * sizeof(void*)); // plus one unused selector, implemented as pointer to the default impl
//////////
//////////NOTE: unexpected behaviour confirmed with GCC-8
//////////
////////// However, the practice of verifying data size and layout assumptions
////////// is increasingly questionable, given that all modern compilers do data flow based optimisations.
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1007

View file

@ -1,182 +0,0 @@
/*
MaybeValue(Test) - considerations for dealing with optional values
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file maybe-value-test.cpp
** unit test \ref MaybeValue_test
*/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/maybe.hpp"
#include "lib/util.hpp"
//#include <functional>
//#include <cstdlib>
namespace lib {
namespace test{
namespace error = lumiera::error;
// using util::isSameObject;
// using std::rand;
using util::isnil;
using error::LUMIERA_ERROR_BOTTOM_VALUE;
namespace { // test data and helpers...
uint INVOCATION_CNT(0);
/** helper for testing delayed evaluation */
template<typename VAL>
class Delayed
{
VAL v_;
public:
Delayed (VAL val) : v_(val) { }
VAL
operator() () const
{
++INVOCATION_CNT;
return v_;
}
};
template<typename VAL>
inline Delayed<VAL>
yield (VAL val)
{
}
}
/***********************************************************************************//**
* @test Investigate various situations of using a Maybe value or option monad.
* @note this is a testbed for experiments for the time being 11/2011
*
* @deprecated as of 2016 : the upcoming C++17 will provide an optional type!!!!!
*
* @see lib::Maybe
* @see null-value-test.cpp
* @see util::AccessCasted
*/
class MaybeValue_test : public Test
{
void
run (Arg)
{
show_basicOperations();
show_delayedAccess();
}
void
show_basicOperations()
{
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856
Maybe<int> one(1);
Maybe<int> opt(5);
Maybe<int> nil;
CHECK (opt); CHECK (!isnil(opt));
CHECK (!nil); CHECK ( isnil(nil));
// access the optional value
CHECK (1 == *one);
CHECK (5 == *opt);
// can't access an bottom value
VERIFY_ERROR (BOTTOM_VALUE, *nil);
// flatMap operation (apply a function)
CHECK (7 == *(opt >>= inc2));
CHECK (9 == *(opt >>= inc2 >>= inc2));
// alternatives
CHECK (1 == *(one || opt));
CHECK (5 == *(nil || opt));
CHECK (1 == *(nil || one || opt));
CHECK (1 == one.get());
CHECK (1 == one.getOrElse(9));
CHECK (9 == nil.getOrElse(9));
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856
}
void
show_delayedAccess()
{
INVOCATION_CNT = 0;
Maybe<int> nil;
Maybe<int> two(2);
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856
Maybe<int(void)> later(yield(5));
CHECK (0 == INVOCATION_CNT);
CHECK (2 == *(two || later));
CHECK (0 == INVOCATION_CNT);
CHECK (5 == *(nil || later));
CHECK (1 == INVOCATION_CNT);
later.get();
CHECK (2 == INVOCATION_CNT);
CHECK (2 == two.getOrElse(later));
CHECK (2 == INVOCATION_CNT);
CHECK (5 == nil.getOrElse(later));
CHECK (3 == INVOCATION_CNT);
// obviously, this also works just with a function
CHECK (7 == nil.getOrElse(yield(7)));
CHECK (4 == INVOCATION_CNT);
// stripping the delayed evaluation
Maybe<int> some = later;
CHECK (5 == INVOCATION_CNT);
CHECK (5 == some);
CHECK (5 == INVOCATION_CNT);
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856
}
};
/** Register this test class... */
LAUNCHER (MaybeValue_test, "unit common");
}} // namespace lib::test

View file

@ -1,117 +0,0 @@
/*
ThreadLocal(Test) - verify wrapper for using thread-local data
Copyright (C) Lumiera.org
2011, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file thread-local-test.cpp
** unit test \ref ThreadLocal_test
*/
#include "lib/test/run.hpp"
#include "vault/thread-wrapper.hpp"
#include "lib/thread-local.hpp"
#include <cstdlib>
using test::Test;
using vault::ThreadJoinable;
using std::rand;
namespace lib {
namespace test{
namespace { // private test setup...
const uint NUM_THREADS = 50;
const uint MAX_RAND = 5*1000*1000;
/** Subject of the test */
ThreadLocalPtr<uint> privateValue;
struct TestThread
: ThreadJoinable
{
TestThread()
: ThreadJoinable("test Thread-local storage"
,verifyThreadLocal)
{ }
/** the actual test operation running in a separate thread */
static void
verifyThreadLocal()
{
uint secret (1 + rand() % MAX_RAND);
privateValue.set (&secret);
usleep (secret); // sleep for a random period
if (secret != *privateValue)
throw error::Fatal ("thread-local value access broken");
}
};
} // (End) test setup....
/**********************************************************************//**
* @test use a wrapper to simplify handling of thread-local data.
* Create some threads, each referring to another piece of data
* through the "same" wrapper instance.
*
* @see vault::Thread
* @see lib::ThreadLocal
*/
class ThreadLocal_test : public Test
{
virtual void
run (Arg)
{
TestThread testcase[NUM_THREADS] SIDEEFFECT;
for (uint i=0; i < NUM_THREADS; ++i)
CHECK (testcase[i].join().isValid() );
}
};
/** Register this test class... */
LAUNCHER (ThreadLocal_test, "function common");
}} // namespace vault::test