MERGE: upgrade to Debian/Buster and to C++17

This commit is contained in:
Fischlurch 2020-02-22 02:16:25 +01:00
commit b2b5cf0f6d
66 changed files with 1538 additions and 1895 deletions

View file

@ -0,0 +1,5 @@
This is the Public Key of the Package signing GPG key
used for building of Debian DEB packages on Lumiera.org
Fingerprint: D0333C21252B66D54A4CC1D265B66B07A1DE94B2
UID: Hermann Vosseler <deb@ichthyostega.de>

View file

@ -0,0 +1,61 @@
#!/bin/sh
#
# build_lumiera-build-dependencies.sh - prepare/build on a pristine Debian/Ubuntu system (Docker)
#
#
#------------------------------------------------------Setup
# The Debian DEB Depot to retrieve the source packages
DEBIAN_REPO="http://lumiera.org/debian/"
SRC_DISTRO="stretch"
REPO_SECTION="experimental"
#
# GPG pubkey to trust for signed packages
TRUSTED_GPG=$(dirname $0)/deb.lumiera.org.PUB.gpg
#
# Directory for building DEB packages
PACK_DIR=./pack
#
#------------------------------------------------------Setup(End)
#
fail() {
echo "\nFAIL: $1\n\n"
exit 1
}
# Sanity-Checks
[ -f $TRUSTED_GPG ] || fail "GPG Key not found: $TRUSTED_GPG"
mkdir -p $PACK_DIR
[ -w $PACK_DIR ] || fail "Unable to build. Can not write to $PACK_DIR"
# prepare the host system with Lumiera package sources
cp $TRUSTED_GPG /etc/apt/trusted.gpg.d/
chmod a+r-x /etc/apt/trusted.gpg.d/$(basename $TRUSTED_GPG)
echo "deb-src $DEBIAN_REPO $SRC_DISTRO $REPO_SECTION" > /etc/apt/sources.list.d/55-lumiera.list
apt update
apt install -y build-essential ca-certificates
apt update
# build some special build dependencies from source
WORKDIR=$(pwd -P)
cd $PACK_DIR
echo "\n\n======= building NoBug =======\n"
apt-get -y build-dep nobug
apt-get source --compile nobug
dpkg -i nobug-dev*deb libnobug*deb || fail "installing NoBug"
echo "\n\n======= building GDLmm =======\n"
apt-get -y build-dep libgdlmm-3-dev
apt-get source --compile libgdlmm-3-dev || fail "installing GDLmm"
dpkg -i libgdlmm-3*deb
# install the build dependencies of Lumiera from DEB
echo "\n\nInstalling Lumiera build dependencies...\n"
apt-get -y build-dep lumiera
# return to the working directory
cd $WORKDIR
echo "Build-Dependencies of Lumiera successfully installed.\n\n"

Binary file not shown.

View file

@ -0,0 +1,44 @@
#!/bin/sh
#
# docker_open-lumiera-buildenv.sh - launch Docker container with interactive build shell
#
#
#------------------------------------------------------Setup
#
# Working directory path on the host
WORKDIR_HOST="/home/$USER/devel/lumi"
#
# bind-mount of the working dir in the container
WORKDIR_GUEST="/lumi"
#
# Docker image-ID to launch
DOCKER_IMAGE="ubuntu:bionic"
#
# Script code to inject into interactive shell
COMMANDS=$(cat) <<RUN_IN_CONTAINER
. /etc/bash.bashrc
. ~/.bashrc
alias la='ls -latr'
$WORKDIR_GUEST/admin/build_lumiera-build-dependencies.sh
RUN_IN_CONTAINER
#
#------------------------------------------------------Setup(End)
#
# Launch...
# (1) launch the docker container
# (2) start a bash there to process a bash commandline
# (3) this commandline in turn launches an interactive bash
# (4) and this interactive bash gets a startup-Shellscript to perform
# (5) and this shellscript is read from an temporary named pipe to another shellscript
# (6) which in turn prints the script code from the setup-variable $COMMANDS
#
# bash magic thanks to Jonathan Potter; see https://serverfault.com/a/586272
#
#
docker run -v $WORKDIR_HOST:$WORKDIR_GUEST -it $DOCKER_IMAGE bash -c "bash --rcfile <(echo \"${COMMANDS}\" )"

View file

@ -92,8 +92,6 @@ def configure(env):
problems.append('We need the boost::system support library (including binary lib).')
if not conf.CheckLibWithHeader('boost_filesystem','boost/filesystem.hpp','C++'):
problems.append('We need the boost::filesystem lib (including binary lib for linking).')
if not conf.CheckLibWithHeader('boost_regex','boost/regex.hpp','C++'):
problems.append('We need the boost regular expression lib (incl. binary lib for linking).')
if not conf.CheckPkgConfig('gavl', '1.4'):

View file

@ -74,7 +74,7 @@ def defineBuildEnvironment():
env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root
, CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines
, CCFLAGS='-Wall -Wextra -Wformat-security'
, CXXFLAGS='-std=gnu++14 -Wno-enum-compare -Wno-noexcept-type'
, CXXFLAGS='-std=gnu++17 -Wno-enum-compare'
, CFLAGS='-std=gnu99'
)
env.Append(LINKFLAGS='-Wl,--no-undefined') # require every dependency is given on link, in the right order

View file

@ -1,7 +1,7 @@
Dependencies
============
Build Dependencies
==================
:Author: core-devs
:Date: 11/2015
:Date: 2/2020
:toc:
@ -54,30 +54,32 @@ Languages and Tools
* C / C++
- a C99 / C++14 compatible compiler footnote:[in practice, we build using GCC and
- a C99 / C++17 compatible compiler footnote:[in practice, we build using GCC and
occasionally we check using Clang]
- GCC *4.9* or Clang *3.5* should be fine footnote:[basically we try to use just the stock language.
- GCC *7* or Clang *6* should be fine footnote:[basically we try to use just the stock language.
On rare occasions in the past, we _did_ use some GCC extensions, like `typeof()`, but we care for
workarounds, in case this becomes a problem. Incidentally, `typeof()` is obsoleted by the new
C++ standard, which provides `decltype()` for this purpose.].
* BOOST (listed below are the Debian package names)
- libboost-dev (at least *1.55*)
- libboost-dev (at least *1.67*)
- libboost-program-options-dev
- libboost-program-options-dev
- libboost-filesystem-dev
- libboost-regex-dev
* Script languages
- Python (*2.7*) for build scripts
- Python (*2.x*) might still be handy for build scripts.footnote:[SCons supports both Python 2.x
and Python 3.x transparently. Basically there is no reason to stick to Python 2.x -- just the standard
python is not yet switched on Debian/stable, and so we might miss out on some scripts.]
- bash (some test scripts use bash specific extensions)
Build Tools
~~~~~~~~~~~
* Git
* SCons *2.0* footnote:[we build with SCons >= 2.0 since a long time. Previously the buildsystem used to work with 1.0
In addition, we use some SCons plug-ins, which are all shipped in-tree (`admin/scons`)]
* SCons *2.0* footnote:[we build with SCons >= 3.0 since a long time. However, since the times of 2.0,
there are no major breaking improvements we rely on -- so chances are that the Build works fine with
older versions. In addition, we use some SCons plug-ins, which are all shipped in-tree (`admin/scons`)]
* pkg-config
* Doxygen

View file

@ -4,14 +4,14 @@ Lumiera build system
As work progresses, we will add more information on the Lumiera build system.
//Menu: label Build System
//Menu: prepend child 'Dependencies'
//Menu: prepend child 'BuildDependencies'
//Menu: prepend child 'SCons'
build -- continuous integration -- packaging
* link:SCons.html[Buildsystem]
* link:Dependencies.html[Dependencies]
* link:BuildDependencies.html[Lumiera Build Dependencies]
* link:BuildDroneDraft.html[»Builddrone« concept from 2008]
* Packaging: link:LumieraDebianPackage.html[Debian] RPM
* Lumiera link:../infra/debianDepot.html/[debian depot]

View file

@ -12,8 +12,8 @@ language and compiler support wasn't ready for what we consider _state of the cr
amended deficiencies by rolling our own helper facilities, with a little help from Boost.
Thus there was no urge for us to adopt the new language standard; we could simply wait for
the compiler support to mature. In spring 2014, finally, we were able to switch our codebase
to C++11 with minimal effort.footnote:[since 8/2015 -- after the switch to Debian/Jessie
as a »reference platform«, we even compile with `-std=gnu++14`]
to C++11 with minimal effort.footnote:[since 2/2020 -- after the switch to Debian/Buster
as a »reference platform«, we even compile with `-std=gnu++17`]
Following this switch, we're now able to reap the benefits of
this approach; we may now gradually replace our sometimes clunky helpers and workarounds
with the smooth syntax of the ``new language'' -- without being forced to learn or adopt
@ -201,28 +201,11 @@ August 2015::
our »reference system« (platform) is Debian/Jessie from now on.
We have switched to **C\+\+14** and use (even require) GCC-4.9 or CLang 3.5 -- we can expect solid support
for all C\+\+11 features and most C++14 features.
February 2020::
our »reference system« (platform) is Debian/Buster from now on.
We have switched to **C\+\+17** and use (even require) GCC-8 or CLang 7 -- we can expect solid support
for all C\+\+17 features.
Perfect forwarding
~~~~~~~~~~~~~~~~~~
Unfortunately, we ran into nasty problems with both GCC-4.7 and CLang 3.0 here, when chaining several forwarding calls.
- the new _reference collapsing rules_ seem to be unreliably still. Note that even the standard library uses an
overload to implement `std::forward`, while in theory, a single definition should work for every case.
- in one case, the executable generated by GCC passed a reference to an temporary, where it should have
passed a rvalue reference (i.e. it should have _moved_ the temporary, instead of referring to the
location on stack)
- CLang is unable to pass a plain-flat rvalue through a chain of templated functions with rvalue references.
We get the inspiring error message ``binding of reference to type `std::basic_string<char>` to a value of
type `std::basic_string<char>` drops qualifiers''
Thus -- as of 9/2014 -- the _rules of the game_ are as folows
- it is OK to take arguments by rvalue reference, when the type is explicit
- it is OK to use std::forward _once_ to pass-trough a templated argument
- but the _time is not yet ready_ to get rid of intermediary copies
- we still prefer returning by value (eligible for RVO) and copy-initialisation
- we refrain from switching our metaprogramming code from Loki-Typelists and hand-written specialisations
to variadic templates and `std::tuple`

View file

@ -7,46 +7,6 @@ nonportable hacks, terrorism and other misdemeanour_
Library
-------
Equality of Functors
~~~~~~~~~~~~~~~~~~~~
One of the more important recent additions to the C++ language are function objects.
In addition to the features actually provided by the boost implementation, the tr1 report
also requires function instances to implement an equality operator. Unfortunately the
implementation approach choosen by boost makes a 100% correct implementation of
comparision very dificult, if not impossible. Thus, the boost developers refused
to implement this feature.
The bad news is that really using the power of opaque function objects quickly drove
us (Lumiera) into a situation where such an equalty test and a hash calculation on
function objects would be necessary. The whole point of using function objects is
the ability to ``erase'' specific details, which has the downside that the resulting
generic objects are opaque and often dificult to manage, when it comes to storing
and retrieving objects building on such functors.
Thus I built an hack, based on the implementation details of boost::function.
In +functor-util.hpp+ we define a +class HijackedFunction+, which has the same
data layout as the original boost::function. After forcibly casting such an function
(reference or pointer) into a +HijackedFunction+, we're able to inspect and evaluate
the implementation pointers for equality comparison and hash value calculation.
This approach works and actually detects copied functions to be _equal_, but is
unable to pinpoint _equivalence_, e.g. functors bound to the same function with
the same arguments through separate but otherwise identical invocations of +bind+.
Besides, should boost or the standard library implementors eventually change the
implementation, this workaround will break.
post C++11
^^^^^^^^^^
As of this writing (1/2016), this still remains an unsolved issue. +
The new standard _does indeed require a comparison_, but only a comparison against
`nullptr`, which is equivalent to the `bool` conversion. It seems we need more.
WARNING: what we have here is a really evil hack, and even the notion of ``equality''
is not correct. We admit that real equality can not be achieved with something
like `std::function`, but in spite of that, we do have our own needs.
[red]#TODO# investigate what we _really_ need and come up with a better concept...
Binding Placeholders
~~~~~~~~~~~~~~~~~~~~
The standard allows function objects to be partially closed; this is achieved by

View file

@ -29,7 +29,7 @@ There is nothing really fancy with the configuration, things work as you'd expec
actually enabled. This provider is used by the IDE to retrieve definitions from the compiler for syntax checks
while you type and for building the search index. Typically, this provider is called ``GCC Builtin Compiler Settings'';
please ensure in the global (or the project specific) configuration that the invocation command line includes
the argument `-std=gnu++14` -- otherwise you'll get a lot of red squiggles on the new language features ;-)
the argument `-std=gnu++17` -- otherwise you'll get a lot of red squiggles on the new language features ;-)
Indexer troubleshooting
~~~~~~~~~~~~~~~~~~~~~~~
@ -55,6 +55,20 @@ was able to see the right files with the right locations
_manually_ -- more so if they are still accessible on the system. Dont blame the Indexer, which basically
has no way to find out he is looking at the obsolete source files...
Sourcecode for relevant external libraries
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Especially when working on the GUI, it can be handy to have some of the most relevant libraries around:
`gdl`, `gdlmm3`, `glib2.0`, `glibmm2.4`, `gtk+3.0`, `gtkmm3.0`. Just extract the source code into a directory
and add it via the ``Makefile Project with Existing Code'' wizard. Of course it helps to some degree if you're
also able to _build_ that code (even partially) from within the IDE, since the indexer is than able to pick up
more cross linking information. However, this is not a strict requirement -- even while `F3` often fails, the
``Open Type'' dialog is able to spot the definition in many cases non the less, and when this fails, you can
still use ``brute-force'' file search. What turns out to be much more an impediment in practice is the fact
that you'll have to jump through that C++ binding layer, and you need to pick up some basic knowledge how
this layer works to wrap the underlying plain-C GTK entities; don't confuse the C++ _wrapper objects_
with the _gobject_ (a concept from GLib) used by GTK.
Debugger Breakpoints not working
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This seems to be a FAQ since Eclipse Kepler: If some or all breakpoints just refuse to work and you see

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

@ -1,6 +1,6 @@
Building Lumiera from source
============================
:Date: 2013
:Date: 2020
:toc:
At the moment you can build Lumiera, start the standard Lumiera GUI and run the
@ -40,13 +40,13 @@ 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
system aborts, indicating that a never version of some library is required,
then usually the build system is right...]
More specifically, you'll need the GNU C/C\++ compiler with C++14 support (Version >= 4.9)
More specifically, you'll need the GNU C/C\++ compiler with C++17 support (Version >= 7)
in addition to the following tools and libraries:
* link:http://git-scm.com/[Git] (version management system)
@ -63,12 +63,6 @@ The GUI depends on the following:
* link:https://wiki.gnome.org/LibRsvg[lib rSVG]
* link:https://git.gnome.org/browse/gdl[lib GDL]
CAUTION: there are known problems with *GCC-5.x* as of 11/2015 +
on recent distributions (Ubuntu/wily, Debian/stretch) you might
encounter failing tests.footnote:[these problems aren't really serious;
basically we're sometimes checking mangled class/type names, and seemingly
the mangling behaviour of GCC has changed slightly. We're working on that...]
TIP: Generally speaking, when you want to build software, you'll need the
_development_ version of the packages that contain the headers and pre-built
libraries to link against. These packages are usually named `-devel` or `-dev`.
@ -81,17 +75,6 @@ libboost-dev libboost-program-options-dev libboost-regex-dev libboost-filesystem
libgavl-dev libgtkmm-3.0-dev libgdl-3-dev librsvg2-dev libxv-dev
-------------------------------------------------------------------------------------
Ubuntu note::
some people reported you need to install the `intltool` package from the standard
Ubuntu repository (for this reason it is included in the above collection)
Mint-17.2 (Rafaela) and Ubuntu 12.LTS::
we really need the gcc-4.9, so building on these platforms is a bit tricky. See our
link:{ldoc}/technical/howto/backporting.html#_building_on_mint_17_2_rafaela_8201_8212_8201_gcc_and_libstdc_4_9[»Backporting«]
page for detailed info...
GCC-5.0::
we're aware of some changes in mangled names (or type-IDs), which cause some tests to fail.
Other than that, compilation worked for us.
Build Directory

View file

@ -268,7 +268,6 @@ Steam Layer::
* implement a command dispatcher to allow blocking and queuing of commands
* develop a scheme how to code the GUI commands in practice
- implement a symbol table for the `lib::Symbol` datatype
- provide the foundation for symbolic rules and script bindings
* expand on the work done for symbolic and hash references

View file

@ -36,16 +36,16 @@
#include <boost/functional/hash.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <regex>
using lib::Literal;
using util::isnil;
using boost::regex;
using boost::smatch;
using boost::sregex_iterator;
using boost::match_continuous;
using std::regex;
using std::smatch;
using std::sregex_iterator;
using std::regex_constants::match_continuous;
using boost::hash_combine;
using boost::lexical_cast;

View file

@ -36,7 +36,6 @@
#include "lib/sync.hpp"
#include "lib/error.hpp"
#include "lib/depend.hpp"
#include "lib/functor-util.hpp"
#include "common/instancehandle.hpp"
#include "common/option.hpp"

View file

@ -30,15 +30,14 @@
#include "lib/util.hpp"
#include "include/logging.h"
#include "lib/cmdline.hpp"
#include "lib/format-util.hpp"
#include <boost/regex.hpp>
#include <boost/algorithm/string/join.hpp>
using boost::regex;
using boost::smatch;
using boost::regex_search;
using boost::algorithm::join;
#include <regex>
using std::regex;
using std::smatch;
using std::regex_search;
using util::join;
using util::noneg;
@ -64,12 +63,12 @@ namespace lib {
*/
Cmdline::Cmdline (const string cmdline)
{
regex tokendef("[^ \r\n\t]+");
static regex TOKENDEF{"\\S+"};
smatch match;
string::const_iterator it = cmdline.begin();
string::const_iterator end = cmdline.end();
while (regex_search(it, end, match, tokendef))
while (regex_search(it, end, match, TOKENDEF))
{
string ss(match[0]);
this->push_back(ss);

View file

@ -27,6 +27,7 @@
** the referred data into a vector of strings. Thus `Cmdline` is a way to
** express explicitly on APIs that we are consuming commandline contents,
** and, moreover, it offers a way more sane interface to deal with those.
** @see CmdlineWrapper_test
*/

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

@ -1,190 +0,0 @@
/*
FUNCTOR-UTIL.hpp - collection of helpers for dealing with functors and signals
Copyright (C) Lumiera.org
2008, 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 functor-util.hpp
** Collection of small helpers and utilities related to function objects.
**
** @todo combine with meta/function-closure.hpp and reorganise
** @todo 2017 find out to what extent we still need that (looks obsolete)
**
** @see GuiSubsysDescriptor#start (guifacade.cpp)
** @see MementoTie
** @see functor-util-test.cpp
**
*/
#ifndef FUNCTOR_UTIL_H
#define FUNCTOR_UTIL_H
#include "lib/hash-value.h"
#include <functional>
#include <boost/functional/hash.hpp>
namespace util { ////////////TODO: refactor namespace. But probably not directly into namespace lib. Needs some more consideration though
using std::function;
using std::bind;
using std::placeholders::_1;
namespace { // hiding some nasty details...
using lib::HashVal;
using boost::hash_combine;
/**
* This Class is used to bypass the access protection
* and break into the tr1::function implementation.
* Thus we can implement a raw comparison function,
* as a replacement for the missing functor comparison
* facility. (TR1 requires an operator==, but boost
* seemingly doesn't provide it, because it can't
* be done correctly/properly in all cases. See
* the section "FAQ" in the documentation of
* the boost/function library)
*
* The layout of this class is chosen to mimic that
* of the boost function implementation, without all
* the generic type decoration. For the comparison
* we use a conservative approach, by requiring
* the concrete invoker, the storage manager and
* the actual function and argument data pointers
* to be the same.
* @todo 2017 is this still necessary today?
*/
class HijackedFunction
: std::_Function_base
{
typedef void (*DummyInvoker) (void);
DummyInvoker invoker_;
public:
friend bool
operator== (HijackedFunction const& f1,
HijackedFunction const& f2)
{
return (f1.invoker_ == f2.invoker_)
&& (f1._M_manager == f2._M_manager)
&& (f1._M_functor._M_unused._M_const_object ==
f2._M_functor._M_unused._M_const_object );
} // note: we don't cover any member pointer offset
friend HashVal
hash_value (HijackedFunction const& fun)
{
HashVal hash(0);
hash_combine (hash, fun.invoker_);
hash_combine (hash, fun._M_manager);
hash_combine (hash, fun._M_functor._M_unused._M_const_object);
return hash; // note: member pointer offset part uncovered
}
};
}
/** temporary workaround: tr1/functional should define
* public comparison operators for functor objects, but
* in the implementation provided by boost 1.34 it doesn't.
* To get at least \em some comparison capability, we do a
* brute force comparison of the functor's internal data.
* @note use with caution. This implementation relies on
* internal details of boost/function; but it is
* rather conservative and might deem functors
* "different" erroneously, due to garbage in
* the internal functor data's storage */
template<typename SIG>
inline bool
rawComparison (function<SIG> const& f1,
function<SIG> const& f2)
{
typedef HijackedFunction const& Hij;
return reinterpret_cast<Hij> (f1)
== reinterpret_cast<Hij> (f2);
}
/** catch-all for the comparison: functors with
* different base type are always "different" */
template<typename SIG1, typename SIG2>
inline bool
rawComparison (function<SIG1> const&,
function<SIG2> const&)
{
return false;
}
/** variant with unchecked access */
inline bool
rawComparison (void* f1, void* f2)
{
typedef HijackedFunction * HijP;
return (!f1 && !f2)
|| *reinterpret_cast<HijP> (f1)
== *reinterpret_cast<HijP> (f2);
}
/** workaround to calculate a hash value for a given function object.
* @note use with caution. This implementation relies on internal details
* of boost/function; it can be expected to be rather conservative,
* i.e. yielding different hash values for objects, which actually
* are semantically equivalent.
* @warning especially function objects bound to member functions aren't
* fully supported. It \em may happen that we miss differences on the
* offset part and only hash the "this" pointer on some platform.
*/
template<typename SIG>
inline HashVal
rawHashValue (function<SIG> const& fun)
{
typedef HijackedFunction const& Hij;
return hash_value (reinterpret_cast<Hij> (fun));
}
} // namespace util
namespace std {
/** inject into std::tr1 to be picked up by ADL:
* @return hash value of given functor
* @note use with caution. Hash is calculated
* relying on undocumented boost internals.
*/
template<typename SIG>
inline lib::HashVal
hash_value (function<SIG> const& fun)
{
return util::rawHashValue (fun);
}
}
#endif /*FUNCTOR_UTIL_H*/

View file

@ -577,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

@ -58,7 +58,6 @@
#include "lib/util.hpp"
#include "lib/error.hpp"
#include "lib/opaque-holder.hpp"
#include "lib/functor-util.hpp"
#include <functional>
@ -140,14 +139,6 @@ namespace meta{
{
return get<function<SIG>>();
}
friend bool
operator== (StoreFunction const& o1,
StoreFunction const& o2)
{
return util::rawComparison (o1.asBase(),o2.asBase());
}
};
@ -179,16 +170,6 @@ namespace meta{
REQUIRE (fun);
return *fun;
}
friend bool
operator== (StoreFunPtr const& o1,
StoreFunPtr const& o2)
{
void * *fun1 = reinterpret_cast<void**> (o1.asBase());
void * *fun2 = reinterpret_cast<void**> (o2.asBase());
return *fun1 == *fun2;
}
};
@ -224,12 +205,6 @@ namespace meta{
explicit operator bool() const { return funP_; }
bool isValid() const { return funP_; }
friend bool
operator== (StoreUncheckedFunPtr const& o1,
StoreUncheckedFunPtr const& o2)
{
return unConst(o1).funP_ == unConst(o2).funP_;
}
};

View file

@ -129,6 +129,13 @@ namespace meta{
using Sig = RET(ARGS...);
using Functor = std::function<Sig>;
};
/** Specialisation to strip `noexcept` from the signature */
template<typename RET, typename...ARGS>
struct _Fun<RET(ARGS...) noexcept>
: _Fun<RET(ARGS...)>
{ };
/** Specialisation for using a function pointer */
template<typename SIG>
struct _Fun<SIG*>
@ -153,6 +160,12 @@ namespace meta{
: _Fun<RET(ARGS...)>
{ };
/** Specialisation to deal with member pointer to noexcept function */
template<class C, typename RET, typename...ARGS>
struct _Fun<RET (C::*) (ARGS...) noexcept>
: _Fun<RET(ARGS...)>
{ };
/** Specialisation to handle member pointer to const function;
* indirectly this specialisation also handles lambdas,
* as redirected by the main template (via `decltype`) */

View file

@ -30,15 +30,11 @@
#define LIB_META_MAYBE_COMPARE_H
#include "lib/functor-util.hpp"
#include <functional>
namespace lib {
namespace meta{
using std::function;
/**
* Trait template for invoking equality comparison.
@ -54,19 +50,7 @@ namespace meta{
}
};
/** while the boost function implementation doesn't provide comparison,
* we'll use our private hack, which at least detects equivalence
* in \em some cases... */
template<typename SIG>
struct Comparator<function<SIG>>
{
static bool
equals (function<SIG> const& f1, function<SIG> const& f2)
{
return util::rawComparison(f1,f2);
}
};
/* == add specialisations here == */
template<typename X>

View file

@ -477,6 +477,18 @@ namespace meta {
&& HasFunSig_end<Type>::value
};
};
struct is_noexcept_iterable
{
META_DETECT_NESTED(iterator);
META_DETECT_FUNCTION(typename X::iterator, begin,(void) noexcept);
META_DETECT_FUNCTION(typename X::iterator, end ,(void) noexcept);
enum { value = HasNested_iterator<Type>::value
&& HasFunSig_begin<Type>::value
&& HasFunSig_end<Type>::value
};
};
struct is_const_iterable
{
@ -490,10 +502,24 @@ namespace meta {
};
};
struct is_const_noexcept_iterable
{
META_DETECT_NESTED(const_iterator);
META_DETECT_FUNCTION(typename X::const_iterator, begin,(void) const noexcept);
META_DETECT_FUNCTION(typename X::const_iterator, end ,(void) const noexcept);
enum { value = HasNested_const_iterator<Type>::value
&& HasFunSig_begin<Type>::value
&& HasFunSig_end<Type>::value
};
};
public:
enum { value = is_iterable::value
|| is_const_iterable::value
or is_const_iterable::value
or is_noexcept_iterable::value
or is_const_noexcept_iterable::value
};
};
@ -516,6 +542,18 @@ namespace meta {
};
};
struct is_noexcept_backIterable
{
META_DETECT_NESTED(reverse_iterator);
META_DETECT_FUNCTION(typename X::reverse_iterator, rbegin,(void) noexcept);
META_DETECT_FUNCTION(typename X::reverse_iterator, rend ,(void) noexcept);
enum { value = HasNested_reverse_iterator<Type>::value
&& HasFunSig_rbegin<Type>::value
&& HasFunSig_rend<Type>::value
};
};
struct is_const_backIterable
{
META_DETECT_NESTED(const_reverse_iterator);
@ -528,10 +566,24 @@ namespace meta {
};
};
struct is_const_noexcept_backIterable
{
META_DETECT_NESTED(const_reverse_iterator);
META_DETECT_FUNCTION(typename X::const_reverse_iterator, rbegin,(void) const noexcept);
META_DETECT_FUNCTION(typename X::const_reverse_iterator, rend ,(void) const noexcept);
enum { value = HasNested_const_reverse_iterator<Type>::value
&& HasFunSig_rbegin<Type>::value
&& HasFunSig_rend<Type>::value
};
};
public:
enum { value = is_backIterable::value
|| is_const_backIterable::value
or is_const_backIterable::value
or is_noexcept_backIterable::value
or is_const_noexcept_backIterable::value
};
};

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

@ -31,14 +31,15 @@
#include "lib/util.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>
#include <functional>
#include <regex>
#include <map>
using std::map;
using boost::regex;
using boost::smatch;
using boost::regex_search;
using boost::sregex_iterator;
using std::regex;
using std::smatch;
using std::regex_search;
using std::sregex_iterator;
using util::contains;
using util::isnil;
@ -49,7 +50,7 @@ namespace lib {
namespace { // local definitions
typedef boost::function<bool(string::value_type)> ChPredicate;
using ChPredicate = std::function<bool(string::value_type)> ;
ChPredicate is_alpha = boost::algorithm::is_alpha();
ChPredicate is_upper = boost::algorithm::is_upper();
@ -79,14 +80,14 @@ namespace lib {
map<Symbol, regex> regexTable;
Literal matchArgument = "\\(\\s*([\\w_\\.\\-]+)\\s*\\),?\\s*";
regex findPredicate (string("(\\w+)")+matchArgument);
Literal MATCH_ARGUMENT = R"~(\(\s*([\w_\.\-]+)\s*\),?\s*)~";
const regex FIND_PREDICATE{string{"(\\w+)"} + MATCH_ARGUMENT};
inline regex&
getTermRegex (Symbol sym)
{
if (!contains (regexTable, sym))
regexTable[sym] = regex (string(sym)+matchArgument);
regexTable[sym] = regex (string(sym)+MATCH_ARGUMENT);
return regexTable[sym];
}
}
@ -146,7 +147,7 @@ namespace lib {
{
uint cnt (0);
sregex_iterator end;
for (sregex_iterator i (q.begin(),q.end(), findPredicate);
for (sregex_iterator i (q.begin(),q.end(), FIND_PREDICATE);
i != end; ++i)
++cnt;
return cnt;

View file

@ -43,6 +43,8 @@ namespace lib {
LUMIERA_ERROR_DEFINE (FILE_NOT_DIRECTORY, "path element points at a file instead of a directory");
using std::regex;
using std::regex_replace;
const regex SearchPathSplitter::EXTRACT_PATHSPEC ("[^:]+");
@ -78,7 +80,7 @@ namespace lib {
static const string expandedOriginDir
= fsys::path (findExePath()).parent_path().string() + "/"; ///////////TICKET #896
return boost::regex_replace(src, PICK_ORIGIN_TOKEN, expandedOriginDir);
return regex_replace(src, PICK_ORIGIN_TOKEN, expandedOriginDir);
}

View file

@ -37,19 +37,15 @@
#include "lib/nocopy.hpp"
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
#include <string>
#include <regex>
namespace lib {
using std::string;
using boost::regex;
using boost::smatch;
using boost::regex_search;
using boost::sregex_iterator;
typedef smatch::value_type const& SubMatch;
using SubMatch = std::smatch::value_type const&;
namespace error = lumiera::error;
namespace fsys = boost::filesystem;
@ -77,10 +73,10 @@ namespace lib {
: util::NonCopyable
{
string pathSpec_;
sregex_iterator pos_,
end_;
std::sregex_iterator pos_,
end_;
static const regex EXTRACT_PATHSPEC;
static const std::regex EXTRACT_PATHSPEC;
public:
SearchPathSplitter (string const& searchPath)

View file

@ -31,7 +31,7 @@
#include <nobug.h>
typedef int
typedef void
(*lumiera_sectionlock_unlock_fn)(void*, struct nobug_flag* flag,
struct nobug_resource_user** handle,
const struct nobug_context ctx);

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

@ -24,7 +24,7 @@
/** @file grid.hpp
** definition of a time grid abstraction for time and timecode handling.
** This interface is the foundation to deal with _quantised_ (grid aligned)
** time values, as is essential for handling of timecode data.
** time values, which is essential for handling of timecode data.
*/
@ -43,12 +43,13 @@ namespace time {
/**
* Abstraction of a value alignment grid.
* Such a grid has an underlying scale (origin and measurement)
* and is comprised of distinct grid intervals, which can be addressed
* by a ordering number, centred at origin with interval number zero.
* The typical example is a 25fps time frame grid, but indeed the
* spacing of the intervals is not necessarily constant. An entity
* defining such a grid provides functions to calculate the
* grid coordinates and to convert back to plain values.
* and is comprised of consecutive grid intervals, joined at the
* _grid points._ These can be addressed by an ordering number,
* centred at origin with grid point number zero.
* The classical example is a 25fps time frame grid, but in fact
* the length of the intervals is not necessarily constant. An
* entity defining such a grid provides functions to calculate
* the grid coordinates and to convert back to plain values.
* This includes a way of rounding to the next lower
* grid point, usable for grid aligning values.
*

View file

@ -55,6 +55,7 @@ extern "C" {
#include <math.h>
#include <limits>
#include <string>
#include <sstream>
#include <boost/rational.hpp>
#include <boost/lexical_cast.hpp>
@ -73,6 +74,9 @@ namespace error = lumiera::error;
namespace lib {
namespace meta {
extern const std::string FAILURE_INDICATOR;
}
namespace time {
@ -175,17 +179,30 @@ namespace time {
}
namespace {
template<typename INT>
string
renderFraction (INT const& frac, Literal postfx) noexcept
try {
std::ostringstream buffer;
if (1 == frac.denominator() or 0 == frac.numerator())
buffer << frac.numerator() << postfx;
else
buffer << frac <<postfx;
return buffer.str();
}
catch(...)
{ return meta::FAILURE_INDICATOR; }
}
/** visual framerate representation (for diagnostics) */
FrameRate::operator string() const
{
return 1==denominator() ? lexical_cast<string> (numerator())+"FPS"
: lexical_cast<string> (numerator())
+ "/"
+ lexical_cast<string> (denominator())
+ "FPS";
return renderFraction (*this, "FPS");
}
/** @internal backdoor to sneak in a raw time value
* bypassing any normalisation and limiting */
TimeValue
@ -247,6 +264,13 @@ namespace time {
}} // namespace lib::Time
namespace util {
string
StringConv<lib::time::FSecs, void>::invoke (lib::time::FSecs val) noexcept
{
return lib::time::renderFraction (val, "sec");
}
} // namespace util
@ -285,7 +309,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time)
gavl_time_t
lumiera_rational_to_time (FSecs const& fractionalSeconds)
{
return rational_cast<gavl_time_t> (lib::time::TimeValue::SCALE * fractionalSeconds);
return rational_cast<gavl_time_t> (fractionalSeconds * int{lib::time::TimeValue::SCALE});
}
gavl_time_t
@ -304,7 +328,7 @@ lumiera_frame_duration (FrameRate const& fps)
throw error::Logic ("Impossible to quantise to an zero spaced frame grid"
, error::LUMIERA_ERROR_BOTTOM_VALUE);
FSecs duration = rational_cast<FSecs> (1/fps);
FSecs duration = 1 / fps;
return lumiera_rational_to_time (duration);
}

View file

@ -36,17 +36,17 @@
#include "lib/util.hpp"
#include "lib/util-quant.hpp"
#include <regex>
#include <functional>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
using std::string;
using util::unConst;
using util::isSameObject;
using util::floorwrap;
using boost::regex;
using boost::smatch;
using boost::regex_search;
using std::string;
using std::regex;
using std::smatch;
using std::regex_search;
using boost::lexical_cast;
namespace error = lumiera::error;
@ -71,8 +71,8 @@ namespace time {
TimeValue
Frames::parse (string const& frameNumber, QuantR frameGrid)
{
static regex frameNr_parser ("(?<![\\.\\-\\d])(-?\\d+)#"); // no leading [.-\d], number+'#'
smatch match;
static regex frameNr_parser{"(?:^|[^\\d\\.\\-])(\\-?\\d+)#"}; // no leading [.-\d], digit+'#'
smatch match; // note: ECMA regexp does not support lookbehind
if (regex_search (frameNumber, match, frameNr_parser))
return frameGrid.timeOf (lexical_cast<FrameCnt> (match[1]));
else
@ -119,17 +119,17 @@ namespace time {
TimeValue
Seconds::parse (string const& seconds, QuantR grid)
{
static regex fracSecs_parser ("(?<![\\./\\-\\d])(-?\\d+)(?:([\\-\\+]\\d+)?/(\\d+))?sec");
//__no leading[./-\d] number [+-] number '/' number 'sec'
static regex fracSecs_parser ("(?:^|[^\\./\\d\\-])(\\-?\\d+)(?:([\\-\\+]\\d+)?/(\\d+))?sec");
//__no leading[./-\d] number [+-] number '/' number 'sec'
#define SUB_EXPR(N) lexical_cast<long> (match[N])
#define SUB_EXPR(N) lexical_cast<int> (match[N])
smatch match;
if (regex_search (seconds, match, fracSecs_parser))
if (match[2].matched)
{
// complete spec with all parts
FSecs fractionalPart (SUB_EXPR(2), SUB_EXPR(3));
long fullSeconds (SUB_EXPR(1));
int fullSeconds (SUB_EXPR(1));
return grid.timeOf (fullSeconds + fractionalPart);
}
else

View file

@ -205,7 +205,7 @@ namespace time {
/** rational representation of fractional seconds
* @warning do not mix up gavl_time_t and FSecs */
typedef boost::rational<long> FSecs;
typedef boost::rational<int64_t> FSecs;
/**
@ -550,12 +550,10 @@ namespace time {
class FrameRate
: public boost::rational<uint>
{
typedef boost::rational<uint> IFrac;
public:
FrameRate (uint fps) ;
FrameRate (uint num, uint denom);
FrameRate (IFrac const& fractionalRate);
FrameRate (boost::rational<uint> const& fractionalRate);
// standard copy acceptable;
@ -572,6 +570,13 @@ namespace time {
operator std::string() const;
};
/** convenient conversion to duration in fractional seconds */
inline FSecs
operator/ (int n, FrameRate rate)
{
return FSecs{ n*rate.denominator(), rate.numerator()};
}
@ -583,7 +588,7 @@ namespace time {
inline NUM
__ensure_nonzero (NUM n)
{
if (n == 0)
if (n == NUM{0})
throw error::Logic ("Degenerated frame grid not allowed"
, error::LERR_(BOTTOM_VALUE));
return n;
@ -614,17 +619,17 @@ namespace time {
inline
FrameRate::FrameRate (uint fps)
: IFrac (__ensure_nonzero(fps))
: boost::rational<uint> (__ensure_nonzero(fps))
{ }
inline
FrameRate::FrameRate (uint num, uint denom)
: IFrac (__ensure_nonzero(num), denom)
: boost::rational<uint> (__ensure_nonzero(num), denom)
{ }
inline
FrameRate::FrameRate (IFrac const& fractionalRate)
: IFrac (__ensure_nonzero(fractionalRate))
FrameRate::FrameRate (boost::rational<uint> const& fractionalRate)
: boost::rational<uint> (__ensure_nonzero(fractionalRate))
{ }
inline double
@ -647,5 +652,16 @@ namespace util {
return 0 == dur;
}
// repeated or forward declaration, see meta/util.hpp
template<typename X, typename COND>
struct StringConv;
/** specialisation: render fractional seconds (for diagnostics) */
template<>
struct StringConv<lib::time::FSecs, void>
{
static std::string
invoke (lib::time::FSecs) noexcept;
};
}
#endif
#endif /*LIB_TIME_TIMEVALUE_H*/

View file

@ -38,7 +38,7 @@
#include "lib/util.hpp"
#include "include/logging.h"
#include <boost/regex.hpp>
#include <regex>
using util::_Fmt;
@ -48,9 +48,9 @@ using lib::time::Duration;
using backend_interface::MediaDesc;
using backend_interface::MediaAccessFacade;
using boost::regex;
using boost::smatch;
using boost::regex_search;
using std::regex;
using std::smatch;
using std::regex_search;
using std::dynamic_pointer_cast;
namespace error = lumiera::error;
@ -66,10 +66,10 @@ namespace asset {
*/
string extractName (const string& path)
{
regex pathname_pattern("([^/\\.]+)(\\.\\w+)?$");
static regex PATHNAME_PATTERN("([^/\\.]+)(\\.\\w+)?$");
smatch match;
if (regex_search (path, match, pathname_pattern))
if (regex_search (path, match, PATHNAME_PATTERN))
return util::sanitise (string (match[1]));
else
return "";

View file

@ -112,7 +112,6 @@ namespace control {
virtual operator string() const =0;
virtual bool isValid () const =0; ///< does this closure hold a valid argument tuple?
virtual bool isCaptured () const =0; ///< does this closure hold captured UNDO state?
virtual bool equals (CmdClosure const&) const =0; ///< is equivalent to the given other closure?
virtual void bindArguments (Arguments&) =0; ///< store a set of parameter values within this closure
virtual void bindArguments (lib::diff::Rec const&) =0; ///< store a set of parameter values, passed as GenNode sequence
virtual void unbindArguments() =0; ///< discard any parameters and return to _unbound state_

View file

@ -29,8 +29,6 @@
** identity and usually located within the (pooled) storage managed by the
** CommandRegistry. Client code gets access to a specific CommandImpl through
** a Command instance, which is a small (refcounting smart-ptr) handle.
**
** //TODO
**
** @see Command
** @see SteamDispatcher
@ -246,26 +244,6 @@ namespace control {
% canUndo()
% (pClo_? string(*pClo_) : util::FAILURE_INDICATOR);
}
friend bool
operator== (CommandImpl const& ci1, CommandImpl const& ci2)
{
return (ci1.do_ == ci2.do_)
// and (ci1.undo_ == ci2.undo_) // causes failure regularly, due to the missing equality on boost::function. See Ticket #294
and (ci1.defaultPatt_ == ci2.defaultPatt_)
and (ci1.canExec() == ci2.canExec())
and (ci1.canUndo() == ci2.canUndo())
and (ci1.pClo_->equals(*ci2.pClo_))
;
}
friend bool
operator!= (CommandImpl const& ci1, CommandImpl const& ci2)
{
return not (ci1==ci2);
}
};

View file

@ -83,21 +83,6 @@ namespace control {
LERR_(UNBOUND_ARGUMENTS));
clo.invoke (func_);
}
/// Supporting equality comparisons...
friend bool
operator== (Mutation const& m1, Mutation const& m2)
{
return (m1.func_ == m2.func_);
}
friend bool
operator!= (Mutation const& m1, Mutation const& m2)
{
return not (m1==m2);
}
};

View file

@ -64,7 +64,6 @@ namespace control {
using lib::meta::NullType;
using lib::meta::buildTuple;
using lib::meta::equals_safeInvoke;
using lib::TypedAllocationManager;
using std::function;
using std::ostream;
@ -96,20 +95,11 @@ namespace control {
/////////////////////////////////////////////////////////////TICKET #798 : we need to pick up arguments from a lib::diff::Record.
ostream&
dump (ostream& output) const
{
return BASE::dump (output << element() << ',');
}
friend bool
compare (ParamAccessor const& p1, ParamAccessor const& p2)
{
return equals_safeInvoke (p1.element(), p2.element())
&& compare ( static_cast<BASE>(p1)
, static_cast<BASE>(p2) );
}
};
template<class TUP, uint n>
@ -135,12 +125,6 @@ namespace control {
{
return output;
}
friend bool
compare (ParamAccessor const&, ParamAccessor const&)
{
return true;
}
};
@ -218,11 +202,6 @@ namespace control {
else
return dumped+")";
}
/// Supporting equality comparisons...
friend bool operator== (OpClosure const& c1, OpClosure const& c2) { return compare (c1.params_, c2.params_); }
friend bool operator!= (OpClosure const& c1, OpClosure const& c2) { return not (c1 == c2); }
};

View file

@ -204,30 +204,6 @@ namespace control {
{
arguments_.template create<ArgHolder>();
}
bool
equals (CmdClosure const& other) const override
{
const SimpleClosure* toCompare = dynamic_cast<const SimpleClosure*> (&other);
return (toCompare)
and (*this == *toCompare);
}
/// Supporting equality comparisons...
friend bool
operator== (SimpleClosure const& a1, SimpleClosure const& a2)
{
return (a1.arguments_->isValid() == a2.arguments_->isValid())
and (*a1.arguments_ == *a2.arguments_)
;
}
friend bool
operator!= (SimpleClosure const& a1, SimpleClosure const& a2)
{
return not (a1 == a2);
}
};

View file

@ -255,31 +255,6 @@ namespace control {
{
return memento_->getState();
}
bool
equals (CmdClosure const& other) const
{
const StorageHolder* toCompare = dynamic_cast<const StorageHolder*> (&other);
return (toCompare)
and (*this == *toCompare);
}
/// Supporting equality comparisons...
friend bool
operator== (StorageHolder const& a1, StorageHolder const& a2)
{
return (a1.arguments_->isValid() == a2.arguments_->isValid())
&& (*a1.arguments_ == *a2.arguments_)
&& (a1.memento_->isValid() == a2.memento_->isValid())
&& (*a1.memento_ == *a2.memento_)
;
}
friend bool
operator!= (StorageHolder const& a1, StorageHolder const& a2)
{
return not (a1 == a2);
}
};

View file

@ -369,13 +369,6 @@ namespace control {
}
bool
Command::equivalentImpl (Command const& c1, Command const& c2)
{
return c1.impl() == c2.impl();
}
Symbol
Command::getID() const noexcept
{

View file

@ -32,8 +32,8 @@
** function arguments need to be provided (this is called "binding" or "closing the function arguments").
** These function arguments are stored within the command definition and remain opaque to the client code
** actually invoking the command. Behind the scenes, there is a CommandRegistry, holding an index of the
** registered commands and managing the storage for command definitions and arguments. The actual
** Command object used by client code is a small, copyable and ref-counting handle to this
** registered commands and managing the storage for command definitions and arguments. The actual
** Command object used by client code is a small, copyable and ref-counting handle to this
** stored definition backend.
**
** # Command definition, argument types and UNDO operation
@ -110,7 +110,7 @@ namespace control {
* Handle object representing a single Command instance to be used by client code.
* Commands are accessed \link #get through a symbolic ID \endlink; there needs to be
* a CommandDef somewhere to specify the actual operation and to define, how the
* effect of the command can be undone. Moreover, the command's definition
* effect of the command can be undone. Moreover, the command's definition
* refers to a HandlingPattern, which describes how the command is actually
* to be executed (the default is scheduling it within the SteamDispatcher)
*
@ -223,7 +223,6 @@ namespace control {
private:
void setArguments (Arguments&);
void setArguments (lib::diff::Rec const&);
static bool equivalentImpl (Command const&, Command const&);
};
@ -312,9 +311,7 @@ namespace control {
operator== (Command const& c1, Command const& c2)
{
return (!c1 && !c2)
|| ( c1 && c2 && ( &c1.impl() == &c2.impl()
|| Command::equivalentImpl (c1,c2)
));
|| ( c1 && c2 && (&c1.impl() == &c2.impl()));
}
inline bool
@ -328,7 +325,7 @@ namespace control {
operator< (Command const& c1, Command const& c2)
{
return (!c1 && c2)
|| ( c1 && c2 && (&c1.impl() < &c2.impl()));
|| ( c1 && c2 && (&c1.impl() < &c2.impl()));
}

View file

@ -44,7 +44,6 @@
#include "lib/meta/function-closure.hpp"
#include "steam/control/command-signature.hpp"
#include "lib/replaceable-item.hpp"
#include "lib/functor-util.hpp"
#include "lib/format-obj.hpp"
#include "lib/util.hpp"
@ -202,20 +201,6 @@ namespace control {
/** for diagnostics: include format-util.hpp */
operator std::string() const;
/// Supporting equality comparisons...
friend bool
operator== (MementoTie const& m1, MementoTie const& m2)
{
return ((!m1.undo_ && !m2.undo_ && !m1.capture_ && !m2.capture_) // either no valid functions
|| ( util::rawComparison(m1.undo_, m2.undo_ ) // or identical functions
&& util::rawComparison(m1.capture_,m2.capture_ )
)
)
&& (m1.isCaptured_ == m2.isCaptured_) // either both not captured or identical state
&& (!m1.isCaptured_
|| equals_safeInvoke (m1.memento_, m2.memento_));
}
};

View file

@ -28,7 +28,7 @@
** "clean up" after usage.
**
** Within the Lumiera Engine, the BufferProvider default implementation utilises instances
** of TypeHandler to \em describe specific buffer types capable of managing an attached object,
** of TypeHandler to _describe specific buffer types_ capable of managing an attached object,
** or requiring some other kind of special treatment of the memory area used for the buffer.
** This BufferDescriptor is embodied into the BufferMetadata::Key and used later on to invoke
** the contained ctor / dtor functors, passing a concrete buffer (memory area).
@ -44,7 +44,6 @@
#include "lib/error.hpp"
#include "lib/hash-value.h"
#include "lib/functor-util.hpp"
#include <functional>
#include <boost/functional/hash.hpp>
@ -84,6 +83,16 @@ namespace engine {
X* embedded = static_cast<X*> (storageBuffer);
embedded->~X();
}
template<typename CTOR, typename DTOR>
inline HashVal
deriveCombinedTypeIdenity()
{
HashVal hash{0};
boost::hash_combine (hash, typeid(CTOR).hash_code());
boost::hash_combine (hash, typeid(DTOR).hash_code());
return hash;
}
}//(End)placement-new helpers
@ -97,8 +106,14 @@ namespace engine {
* special treatment of a buffer space. When defined, the buffer
* will be prepared on locking and cleanup will be invoked
* automatically when releasing.
* @warning comparison and hash values rely on internals of the
* std::function implementation and might not be 100% accurate
* @warning comparison and hash values are based merely on the type
* of the Ctor and Dtor functions -- so all type handlers bound
* to the same functor type count as equivalent. This might not
* be what you'd expect, however, there is no sane way to test
* for equivalence of functors anyway. In the typical usage,
* a TypeHandler will be created by TypeHandler::create<TY>(),
* and thus will be dedicated to a given type to be placed
* into the storage buffer.
*/
struct TypeHandler
{
@ -106,11 +121,13 @@ namespace engine {
DoInBuffer createAttached;
DoInBuffer destroyAttached;
HashVal identity;
/** build an invalid NIL TypeHandler */
TypeHandler()
: createAttached()
, destroyAttached()
, identity{0}
{ }
/** build a TypeHandler
@ -124,6 +141,7 @@ namespace engine {
TypeHandler(CTOR ctor, DTOR dtor)
: createAttached (ctor)
, destroyAttached (dtor)
, identity{deriveCombinedTypeIdenity<CTOR,DTOR>()}
{ }
/** builder function defining a TypeHandler
@ -154,22 +172,14 @@ namespace engine {
friend HashVal
hash_value (TypeHandler const& handler)
{
HashVal hash(0);
if (handler.isValid())
{
boost::hash_combine(hash, handler.createAttached);
boost::hash_combine(hash, handler.destroyAttached);
}
return hash;
return handler.identity;
}
friend bool
operator== (TypeHandler const& left, TypeHandler const& right)
{
return (not left.isValid() and not right.isValid())
|| ( util::rawComparison(left.createAttached, right.createAttached)
&& util::rawComparison(left.destroyAttached, right.destroyAttached)
);
|| (left.identity == right.identity);
}
friend bool
operator!= (TypeHandler const& left, TypeHandler const& right)

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

@ -43,8 +43,8 @@ namespace test{
namespace {
const uint MAX_FRAMES = 25*500;
const uint DIRT_GRAIN = 50;
const int MAX_FRAMES = 25*500;
const int DIRT_GRAIN = 50;
const FSecs F25(1,25); // duration of one PAL frame
@ -91,10 +91,10 @@ namespace test{
{
FixedFrameQuantiser fixQ(25);
uint frames = (rand() % MAX_FRAMES);
int frames = (rand() % MAX_FRAMES);
FSecs dirt = (F25 / (2 + rand() % DIRT_GRAIN));
Time rawTime = Time(frames*F25) + Duration(dirt); ////////////////TICKET #939 : should better use 64bit base type for FSecs ??
Time rawTime = Time(frames*F25) + Duration(dirt);
CHECK (Time( frames *F25) <= rawTime);
CHECK (Time((frames+1)*F25) > rawTime);

View file

@ -144,6 +144,7 @@ namespace test{
Parsing<format::Frames> ("xxx25#xxx") .should_yield (1);
Parsing<format::Frames> ("12 25#") .should_yield (1);
Parsing<format::Frames> ("12 25# 33#") .should_yield (1); // note pitfall: the first valid number is used
Parsing<format::Frames> ("12 25# \n 33#") .should_yield (1);
Parsing<format::Frames> ("12\n 25# \n 33#") .should_yield (1);
Parsing<format::Frames> ("12.25#") .should_fail(); // rejected because of leading dot (ambiguity)
}

View file

@ -52,8 +52,8 @@ namespace test {
namespace { // Test definitions...
const Time testOrigin (12,34);
const FrameRate testFps (5,6);
const Time TEST_ORIGIN (12,34);
const FrameRate TEST_FPS (5,6);
const uint MAX_FRAMES = 1000;
const uint DIRT_GRAIN = 50;
@ -89,8 +89,8 @@ namespace test {
CHECK ( spec.origin == TimeValue(0));
CHECK (!spec.predecessor);
spec.fps = testFps;
spec.origin = testOrigin;
spec.fps = TEST_FPS;
spec.origin = TEST_ORIGIN;
PGrid myGrid = spec.commit();
CHECK (myGrid);
@ -101,15 +101,15 @@ namespace test {
int randomFrame = (rand() % MAX_FRAMES);
Time point (myGrid->timeOf (randomFrame));
CHECK (point == testOrigin + randomFrame * testFps.duration());
CHECK (point == TEST_ORIGIN + randomFrame * TEST_FPS.duration());
uint fract = 2 + rand() % DIRT_GRAIN;
FSecs dirt = rational_cast<FSecs> (1 / testFps / fract);
ASSERT (Time(dirt) < testFps.duration());
int fract = 2 + rand() % DIRT_GRAIN;
FSecs dirt = (1/TEST_FPS) / fract;
ASSERT (Time(dirt) < TEST_FPS.duration());
ASSERT (0 < dirt);
Time dirty(point + Time(dirt));
CHECK (point == testOrigin + myGrid->gridAlign(dirty));
CHECK (point == TEST_ORIGIN + myGrid->gridAlign(dirty));
}

View file

@ -213,7 +213,6 @@ namespace test {
Tracker<string>::instanceCnt = 0;
createTuples (testTuples);
checkArgumentComparison ();
serialiseArgTuples (testTuples);
testTuples.clear();
@ -291,50 +290,6 @@ namespace test {
/** @test verify the comparison operators */
void
checkArgumentComparison ()
{
StorageHolder<void(int,int), int> one, two;
CHECK (one == two); // empty, identically typed argument holders -->equal
one.tie(dummyU,dummyC)
.tieCaptureFunc()(1,9);
CHECK (one != two); // now one contains captured UNDO state
two.tie(dummyU,dummyC)
.tieCaptureFunc()(1,9);
two.memento() = one.memento(); // put the same UNDO state in both
CHECK (one == two); // ...makes them equal again
one.storeTuple (make_tuple (1,2));
CHECK (one != two); // verify argument tuple comparison
CHECK (two != one);
CHECK (!isnil (one));
CHECK ( isnil (two));
two.storeTuple (make_tuple (3,4));
CHECK (!isnil (two));
CHECK (one != two);
CHECK (two != one);
one.storeTuple (make_tuple (1,4));
CHECK (!isnil (one));
CHECK (one != two);
CHECK (two != one);
one.storeTuple (make_tuple (3,4));
CHECK (!isnil (one));
CHECK (one == two);
CHECK (two == one);
two.memento() = 12345;
CHECK (!isnil (two));
CHECK (one != two);
CHECK (two != one);
}
/** @test simulate a complete command lifecycle with regards to the
* storage handling of the command parameters and state memento.
*/

View file

@ -137,7 +137,6 @@ namespace test {
CHECK (orig && copy);
CHECK (orig->canExec());
CHECK (copy->canExec());
CHECK (orig == copy);
// prepare for command invocation on implementation level....
@ -151,13 +150,11 @@ namespace test {
long state_after_exec1 = command1::check_;
CHECK (command1::check_ > 0);
CHECK (orig->canUndo());
CHECK (orig != copy);
CHECK (!copy->canUndo());
testExec.exec (*copy, "Execute clone"); // EXEC 2
CHECK (command1::check_ != state_after_exec1);
CHECK (copy->canUndo());
CHECK (copy != orig);
// invoke UNDO on the clone
testExec.undo (*copy, "Undo clone"); // UNDO 2
@ -166,8 +163,6 @@ namespace test {
// invoke UNDO on original
testExec.undo (*orig, "Undo original"); // UNDO 1
CHECK (command1::check_ ==0);
CHECK (copy != orig);
}
};

View file

@ -98,30 +98,15 @@ namespace test {
/*************************************************************************************//**
* @test cover command equality detection.
* Two commands are deemed equivalent, if they
* - build on the same Mutation functors
* - are either both incomplete or
* - are bound to equivalent arguments
* - hold equivalent undo state (memento)
* To conduct this test, we set up two sets of functions, and then build both complete
* command objects and command implementation facilities based on them.
*
* @note The hidden problem with those comparisons is the equivalence of function objects.
* While required by TR1, unfortunately lib boost refuses to implement functor equality.
* Which forces us to resort to a low level hack, based on internals of the boost function
* implementation. This workaround reliably pinpoints differing functions, but sometimes
* fails to detect equivalent functions under specific circumstances (e.g. when there is
* binding involved, and / or the binders have been cloned). Bottom line: \c == is
* reliable, \c != might be wrong.
* Two commands are deemed equivalent, if they are based on the same CommandImpl record.
* This means, we only rely on the _identity_ of those commands, but do not check the
* _equivalence_ of their backing implementations. The latter can not be possibly
* implemented in a totally airtight fashion, and for this reason, the C++ standard
* decided not to support comparison between std::function objects.
*
* @see control::Command
* @see control::CmdClosure
* @see control::Mutation
* @see control::UndoMutation
* @see control::MementoTie
* @see control::CommandImpl
* @see command-basic-test.hpp
* @see functor-util.hpp functor equality workaround
*/
class CommandEquality_test : public Test
{
@ -133,148 +118,6 @@ namespace test {
CHECK (&capt_1 != &capt_2);
CHECK (&undo_1 != &undo_2);
verifyMutationEquality();
verifyMementoEquality();
verifyClosureEquality();
verifyCommandEquality();
}
void
verifyMutationEquality()
{
Fun_o oFun_1 (oper_1);
Fun_o oFun_2 (oper_2);
Fun_o oFun_empty;
Fun_u uFun_1 (undo_1);
Fun_u uFun_empty;
Mutation mut1 (oFun_1);
Mutation muti (oFun_1);
Mutation mut2 (oFun_2);
CHECK (mut1 == mut1);
CHECK (mut1 == muti);
CHECK (muti == mut1);
CHECK (mut1 != mut2);
CHECK (mut2 != mut1);
CHECK (muti != mut2);
CHECK (mut2 != muti);
Mutation umu (oFun_empty); // empty operation function
CHECK (mut1 != umu);
Mutation mut_u0 (uFun_empty); // empty undo function
CHECK (mut_u0 != umu);
CHECK (mut_u0 != muti);
Mutation mut_u1 (uFun_1);
CHECK (mut_u0 != mut_u1); // function signatures differing
}
void
verifyClosureEquality()
{
ArgHolder a1 (make_tuple ('a'));
ArgHolder a2 (make_tuple ('a'));
ArgHolder a3 (make_tuple ('z'));
CHECK (a1 == a1);
CHECK (a1 == a2);
CHECK (a2 == a1);
CHECK (a1 != a3);
CHECK (a3 != a1);
CHECK (a2 != a3);
CHECK (a3 != a2);
typedef StorageHolder<Sig_oper,string> Storage;
Storage abuff1;
Storage abuff2;
CHECK (abuff1 == abuff2);
TypedArguments<ArgTuple> newArgs (make_tuple ('z'));
abuff1.bindArguments(newArgs);
CHECK (abuff1 != abuff2);
abuff2.bindArguments(newArgs);
CHECK (abuff1 == abuff2);
UndoMutation umu1 (abuff1.tie (undo_1, capt_1));
CHECK (abuff1 != abuff2); // abuff2 isn't tied yet, i.e. has no undo/capture function
UndoMutation umu2 (abuff2.tie (undo_1, capt_1));
CHECK (abuff1 == abuff2); // same capture function, no memento state!
Closure args {make_tuple ('u')};
umu1.captureState(args);
CHECK (abuff1 != abuff2);
umu2.captureState(args);
CHECK (abuff1 == abuff2); // same functions, same memento state
check_ += "fake"; // manipulate the "state" to be captured
umu2.captureState(args); // capture again...
CHECK (abuff1 != abuff2); // captured memento differs!
UndoMutation umu3 (abuff2.tie (undo_1, capt_2));
umu3.captureState(args);
CHECK (abuff1 != abuff2); // differing functions detected
}
void
verifyMementoEquality()
{
Fun_u uFun_1 (undo_1);
Fun_u uFun_2 (undo_2);
Fun_c cFun_1 (capt_1);
Fun_c cFun_2 (capt_2);
Fun_c cFun_empty;
Fun_c empty_c;
MemHolder m11 (uFun_1, cFun_1);
MemHolder m12 (uFun_1, cFun_2);
MemHolder m21 (uFun_2, cFun_empty); // note: unbound capture function
MemHolder m22 (uFun_2, cFun_2);
CHECK (m11 == m11);
CHECK (m12 == m12);
CHECK (m21 == m21);
CHECK (m22 == m22);
CHECK (!(m11 != m11));
CHECK (m11 != m12);
CHECK (m11 != m21);
CHECK (m11 != m22);
CHECK (m12 != m11);
CHECK (m12 != m21);
CHECK (m12 != m22);
CHECK (m21 != m11);
CHECK (m21 != m12);
CHECK (m21 != m22);
CHECK (m22 != m11);
CHECK (m22 != m12);
CHECK (m22 != m21);
MemHolder m22x (m22); // clone copy
CHECK (!m22x);
CHECK (m22 == m22x); // same functions, no state --> equal
m22x.tieCaptureFunc() ('x'); // produce a memento state
CHECK (!isnil (m22x.getState()));
CHECK (m22 != m22x);
m22.tieCaptureFunc() ('x'); // get same value into the memento within m22
CHECK (m22 == m22x);
// document shortcomings on UndoMutation comparisons
UndoMutation umu11 (m11);
UndoMutation umu12 (m11); // note: due to cloning the embedded functor,
CHECK (umu11 != umu12); // our hacked-in comparison operator fails
}
void
verifyCommandEquality()
{
CommandDef (COMMAND1)
.operation (oper_1)
.captureUndo (capt_1)

View file

@ -60,7 +60,7 @@ namespace test {
* interface. Add/remove a command instance to the index, allocate an
* CommandImpl frame and verify it is removed properly on ref count zero.
* @note this test covers the internal bits of functionality,
* not the behaviour of the (integrated) command framework
* not the behaviour of the (integrated) command framework
*
* @see Command
* @see CommandRegistry
@ -75,7 +75,7 @@ namespace test {
virtual void
run (Arg)
run (Arg)
{
CommandRegistry& registry = CommandRegistry::instance();
CHECK (&registry);
@ -124,9 +124,10 @@ namespace test {
// now create a clone, registered under a different ID
Command cmd2 = cmd1.storeDef(TEST_CMD2);
CHECK (cmd2 == cmd1);
cmd2.bind(54321);
CHECK (cmd2 != cmd1);
CHECK (cmd2 != cmd1); // note: while they are equivalent, they are not identical
Command cm2x = cmd2.bind(54321);
CHECK (cm2x != cmd1);
CHECK (cm2x == cmd2);
// this created exactly one additional instance allocation:
CHECK (1+cnt_inst == registry.instance_count());
@ -143,6 +144,10 @@ namespace test {
CHECK (cnt_defs == registry.index_size()); // removed from index
CHECK (1+cnt_inst == registry.instance_count()); //...but still alive
CHECK (cmdX.isAnonymous());
CHECK (cmd2.isAnonymous()); //......they got deached
CHECK (!cmd1.isAnonymous());
// create a new registration..
registry.track(TEST_CMD2, cmd2);
CHECK (registry.queryIndex(TEST_CMD2));
@ -165,6 +170,7 @@ namespace test {
cmdX.close();
CHECK (1+cnt_inst == registry.instance_count());
cmd2.close();
cm2x.close();
CHECK (0+cnt_inst == registry.instance_count()); // ...as long as it's still referred
}
@ -211,7 +217,6 @@ namespace test {
CHECK (2+cnt_inst == registry.instance_count());
CHECK (!isSameObject (*pImpl, *clone));
CHECK (*pImpl == *clone);
CHECK (!pImpl->canExec());
typedef Types<int> ArgType;
@ -220,12 +225,10 @@ namespace test {
CHECK (pImpl->canExec());
CHECK (!clone->canExec()); // this proves the clone has indeed a separate identity
CHECK (*pImpl != *clone);
// discard the first clone and overwrite with a new one
clone = registry.createCloneImpl(*pImpl);
CHECK (2+cnt_inst == registry.instance_count());
CHECK (*pImpl == *clone);
CHECK (clone->canExec());
clone.reset();

View file

@ -224,7 +224,7 @@ namespace test {
CHECK (!c2.canUndo());
CHECK (c2.isAnonymous());
CHECK (c1 == c2);
CHECK (c1 != c2);
CHECK (!isSameObject(c1, c2));
CHECK (0 == command1::check_);
@ -234,7 +234,6 @@ namespace test {
CHECK (randVal == command1::check_);
CHECK ( c1.canUndo());
CHECK (!c2.canUndo());
CHECK (c1 != c2);
c2();
CHECK (randVal + randVal == command1::check_);
@ -271,7 +270,7 @@ namespace test {
CHECK (c4.canUndo());
CHECK (not c4.isAnonymous());
CHECK ( c2.isAnonymous());
CHECK (c4 == c2);
CHECK (c4 != c2); // note: it was stored as independent clone copy
CHECK (c4 != c1);
c4();
CHECK (c4 != c2); // now lives independently from the original

View file

@ -87,7 +87,6 @@ namespace test {
run (Arg)
{
checkStateCapturingMechanism();
verifyComparisons();
}
@ -134,50 +133,6 @@ namespace test {
bound_undo_func(3*rr);
CHECK (testVal == -20 + 3*rr - (5+rr));
}
void
verifyComparisons()
{
function<void(short,int)> u1_fun; // deliberately unbound
function<void(short,int)> u2_fun = undo;
function< int(short)> c1_fun;
function< int(short)> c2_fun = capture;
MemHolder m11 (u1_fun, c1_fun);
MemHolder m12 (u1_fun, c2_fun);
MemHolder m21 (u2_fun, c1_fun);
MemHolder m22 (u2_fun, c2_fun);
CHECK (!m11 && !m12 && !m21 && !m22);
CHECK ( (m11 == m11));
CHECK (!(m11 != m11));
CHECK (m11 != m12);
CHECK (m11 != m21);
CHECK (m11 != m22);
CHECK (m12 != m11);
CHECK (m12 != m21);
CHECK (m12 != m22);
CHECK (m21 != m11);
CHECK (m21 != m12);
CHECK (m21 != m22);
CHECK (m22 != m11);
CHECK (m22 != m12);
CHECK (m22 != m21);
MemHolder m22x (m22); // clone copy
CHECK (!m22x);
CHECK (m22 == m22x); // same functions, no state --> equal
testVal = 0;
m22x.tieCaptureFunc() (1 + (rand() % 9)); // produce a random memento value != 0
CHECK (0 < m22x.getState());
CHECK (m22 != m22x);
m22.tieCaptureFunc() (m22x.getState()); // get the same value into the memento within m22
CHECK (m22 == m22x);
}
};

View file

@ -220,7 +220,7 @@ namespace test {
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
TimeVar frameStart (refPoint);
InvocationInstanceID prevInvocationID(0);
InvocationInstanceID prevInvocationID(0); ///////////////////////////////////////////////////////TICKET #1138 : C++17 requires explicit ctor for initialisation of union
Offset expectedTimeIncrement (1, FrameRate::PAL);
for (uint i=0; i < plannedChunk.size(); ++i )
{

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

@ -218,7 +218,8 @@ namespace test {
struct Verbose
: Silent
{
Verbose(int i) : Silent(i) { }
using Silent::Silent;
virtual ~Verbose() { }
virtual
operator string() const
@ -230,7 +231,7 @@ namespace test {
struct Explosive
: Verbose
{
Explosive(int i) : Verbose(i) { }
using Verbose::Verbose;
operator string() const
{
@ -291,7 +292,7 @@ namespace test {
cout << _Fmt("__%d__") % "1234" << endl;
cout << _Fmt("__%d__") % "0xff" << endl;
VERIFY_ERROR(FORMAT_SYNTAX, _Fmt("%broken"));
VERIFY_ERROR(FORMAT_SYNTAX, _Fmt("%madness"));
}

View file

@ -1,212 +0,0 @@
/*
FunctorUtil(Test) - verifying function object and signal utilities
Copyright (C) Lumiera.org
2009, 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 functor-util-test.cpp
** unit test \ref FunctorUtil_test
*/
#include "lib/test/run.hpp"
#include "lib/functor-util.hpp"
#include <functional>
#include <boost/functional/hash.hpp>
#include <iostream>
using lib::HashVal;
using std::cout;
using std::function;
using boost::hash; // note: only boost::hash allows for easy defining of custom hash functions
namespace util {
namespace test {
namespace {
void fun1 (int i) { cout << "fun1 (" << i << ")\n"; }
void fun2 (int i) { cout << "fun2 (" << i << ")\n"; }
struct Dummy
{
void gummi (int i) { cout << "gummi (" << i << ")\n"; }
};
}
/*****************************************************************//**
* @test verify some aspects of the functor-util's behaviour.
* At times, this is just a scrapbook for new ideas....
*/
class FunctorUtil_test : public Test
{
virtual void
run (Arg)
{
verifyBruteForceComparison();
verifyHashThroughBackdoor();
}
typedef function<void(int)> Fvi;
typedef function<int(void)> Fiv;
typedef function<void(void)> Fvv;
/** @test workaround for the missing functor comparison operator */
void
verifyBruteForceComparison()
{
Fvi f0;
Fvi f1 (fun1);
Fvi f2 (fun2);
CHECK (!rawComparison(f0, f1));
CHECK (!rawComparison(f1, f2));
CHECK (!rawComparison(f0, f2));
Fvi f22 (f2);
CHECK ( rawComparison(f2, f22));
f1 = f2;
CHECK ( rawComparison(f1, f2));
CHECK (!rawComparison(f0, Fvi())); // note: can't detect they are equivalent
CHECK (!rawComparison(f0, Fiv()));
f1 = bind (fun2, _1);
CHECK (!rawComparison(f1, f2));
Dummy dum1, dum2;
Fvi fm1 = bind (&Dummy::gummi, dum1, _1);
Fvi fm2 = bind (&Dummy::gummi, dum2, _1);
Fvv fm3 = bind (&Dummy::gummi, dum1, 23);
Fvv fm4 = bind (&Dummy::gummi, dum1, 24);
Fvv fm5 = bind (&Dummy::gummi, dum2, 24);
Fvv fm6 = bind (&Dummy::gummi, dum2, 24);
CHECK (!rawComparison(f1, fm1));
CHECK (!rawComparison(fm1, fm2));
CHECK (!rawComparison(fm1, fm3));
CHECK (!rawComparison(fm1, fm4));
CHECK (!rawComparison(fm1, fm5));
CHECK (!rawComparison(fm1, fm6));
CHECK (!rawComparison(fm2, fm3));
CHECK (!rawComparison(fm2, fm4));
CHECK (!rawComparison(fm2, fm5));
CHECK (!rawComparison(fm2, fm6));
CHECK (!rawComparison(fm3, fm4));
CHECK (!rawComparison(fm3, fm5));
CHECK (!rawComparison(fm3, fm6));
CHECK (!rawComparison(fm4, fm5)); // note: same argument but different functor instance
CHECK (!rawComparison(fm4, fm6));
CHECK (!rawComparison(fm5, fm6)); // again: can't detect they are equivalent
}
/** @test workaround for missing standard hash
* calculation for functor objects.
* Workaround relying on boost
* implementation internals */
void
verifyHashThroughBackdoor()
{
Fvi f0;
Fvi f1 (fun1);
Fvi f2 (fun2);
Fvi f22 (f2);
hash<Fvi> calculateHash;
CHECK (calculateHash (f0));
CHECK (calculateHash (f1));
CHECK (calculateHash (f2));
CHECK (calculateHash (f22));
HashVal h0 = calculateHash (f0);
HashVal h1 = calculateHash (f1);
HashVal h2 = calculateHash (f2);
HashVal h22 = calculateHash (f22);
CHECK (h0 != h1);
CHECK (h0 != h2);
CHECK (h1 != h2);
CHECK (h2 == h22);
f1 = f2;
h1 = calculateHash (f1);
CHECK (h1 == h2);
CHECK (h1 != h0);
CHECK (h0 != calculateHash (Fvi())); // note: equivalence not detected
// checking functors based on member function(s)
Dummy dum1, dum2;
Fvi fm1 = bind (&Dummy::gummi, dum1, _1);
Fvi fm2 = bind (&Dummy::gummi, dum2, _1);
Fvv fm3 = bind (&Dummy::gummi, dum1, 23);
Fvv fm4 = bind (&Dummy::gummi, dum1, 24);
Fvv fm5 = bind (&Dummy::gummi, dum2, 24);
Fvv fm6 = bind (&Dummy::gummi, dum2, 24);
HashVal hm1 = calculateHash (fm1);
HashVal hm2 = calculateHash (fm2);
hash<Fvv> calculateHashVV;
HashVal hm3 = calculateHashVV (fm3);
HashVal hm4 = calculateHashVV (fm4);
HashVal hm5 = calculateHashVV (fm5);
HashVal hm6 = calculateHashVV (fm6);
CHECK (h1 != hm1);
CHECK (hm1 != hm2);
CHECK (hm1 != hm3);
CHECK (hm1 != hm4);
CHECK (hm1 != hm5);
CHECK (hm1 != hm6);
CHECK (hm2 != hm3);
CHECK (hm2 != hm4);
CHECK (hm2 != hm5);
CHECK (hm2 != hm6);
CHECK (hm3 != hm4);
CHECK (hm3 != hm5);
CHECK (hm3 != hm6);
CHECK (hm4 != hm5);
CHECK (hm4 != hm6);
CHECK (hm5 != hm6); // again: unable to detect the equivalence
}
};
/** Register this test class... */
LAUNCHER (FunctorUtil_test, "unit common");
}} // namespace util::test

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

@ -88,27 +88,6 @@ namespace test {
check_FunctPtrHolder(Efp(testFunc),Efp(&testFunc), Efp(returnIt));
check_VoidPtrHolder(Evoid(testFunc),Evoid(&testFunc),Evoid(returnIt));
check_Comparisons (Efun(testFunc), Efun(bindFunc));
check_Comparisons (Efun(testFunc), Efun(pAplFunc));
check_Comparisons (Efun(testFunc), Efun(membFunc));
check_Comparisons (Efun(testFunc), Efun(getterFunc));
check_Comparisons (Efun(bindFunc), Efun(pAplFunc));
check_Comparisons (Efun(bindFunc), Efun(membFunc));
check_Comparisons (Efun(bindFunc), Efun(getterFunc));
check_Comparisons (Efun(pAplFunc), Efun(membFunc));
check_Comparisons (Efun(pAplFunc), Efun(getterFunc));
check_Comparisons (Efun(membFunc), Efun(getterFunc));
check_Comparisons (Efp(testFunc), Efp(returnIt));
check_Comparisons (Evoid(testFunc), Evoid(returnIt));
CHECK ( detect_Clone (Efun(testFunc)));
CHECK (!detect_Clone (Efun(bindFunc))); //note equality not detected when cloning a bind term
CHECK (!detect_Clone (Efun(pAplFunc))); //similarly
CHECK (!detect_Clone (Efun(membFunc))); //analogous for bound member function
CHECK ( detect_Clone (Efp(testFunc) ));
CHECK ( detect_Clone (Evoid(testFunc)));
detect_unboundFunctor(Efun(testFunc), Efun(getterFunc), Efun(membFunc));
detect_unboundFunctor(Efp(testFunc),Efp(&testFunc), Efp(returnIt));
detect_unboundFunctor(Evoid(testFunc),Evoid(&testFunc),Evoid(returnIt));
@ -194,11 +173,6 @@ namespace test {
CHECK (_sum_ == 10+'a'+20+'b'+30+'c');
CHECK (_sum_ == (f3.getFun<int(void)>()) () );
#if false ////////////////////////////////////////////////////////TODO: restore throwing ASSERT
VERIFY_ERROR (ASSERTION, f1.getFun<int(int)>() );
#endif////////////////////////////////////////////////////////////
}
@ -231,27 +205,6 @@ namespace test {
} // likely to result in heap corruption or SEGV
template<class HOL>
void
check_Comparisons (HOL h1, HOL h2)
{
CHECK (h1 == h1); CHECK (!(h1 != h1));
CHECK (h2 == h2); CHECK (!(h2 != h2));
CHECK (h1 != h2);
CHECK (h2 != h1);
}
template<class HOL>
bool
detect_Clone (HOL const& h1)
{
HOL clone (h1);
return (clone == h1);
}
template<class HOL>
void
detect_unboundFunctor (HOL h1, HOL h2, HOL h3)

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

File diff suppressed because it is too large Load diff