MERGE: upgrade to Debian/Buster and to C++17
This commit is contained in:
commit
b2b5cf0f6d
66 changed files with 1538 additions and 1895 deletions
5
admin/README.deb.lumiera.org.PUB.gpg
Normal file
5
admin/README.deb.lumiera.org.PUB.gpg
Normal 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>
|
||||
61
admin/build_lumiera-build-dependencies.sh
Executable file
61
admin/build_lumiera-build-dependencies.sh
Executable 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"
|
||||
|
||||
BIN
admin/deb.lumiera.org.PUB.gpg
Normal file
BIN
admin/deb.lumiera.org.PUB.gpg
Normal file
Binary file not shown.
44
admin/docker_open-lumiera-buildenv.sh
Executable file
44
admin/docker_open-lumiera-buildenv.sh
Executable 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}\" )"
|
||||
|
||||
|
||||
|
|
@ -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'):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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*
|
||||
+
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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`) */
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
|
|
|
|||
|
|
@ -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 "";
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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_));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 (®istry);
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue