diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt new file mode 100755 index 00000000..5d47acbb --- /dev/null +++ b/COPYRIGHT.txt @@ -0,0 +1,3 @@ +Copyright (C) 2009 Lorenzo Caminiti. +Distributed under DBC++ Software License, Version 1.0 (see LICENSE_1_0.txt). + diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100755 index 00000000..c868f1f6 --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,7 @@ +To install run `make install'. + +The library is composed of C/C++ header files only and does not need to be +built. The library files are contained in the "include/" directory and they are: + "include/dbc.hpp" Main file that includes entire library. + "include/dbc/*" All other header files that implement the library. + diff --git a/LICENSE_1_0.txt b/LICENSE_1_0.txt new file mode 100755 index 00000000..0bbb43ce --- /dev/null +++ b/LICENSE_1_0.txt @@ -0,0 +1,25 @@ +Design By Contract for C++ (DBC++) Software License, Version 1.0 +April 19th, 2009 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + diff --git a/Makefile b/Makefile new file mode 100755 index 00000000..91d775f1 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ + +all: + @echo "Design by Contract for C++ (DbC++)" + @echo + @echo "This library does not need to be build. It is composed only of " + @echo "C/C++ header files contained int the \"./include/\" directory." + @echo + @echo "make install Install DbC++ files onto your system (you might " + @echo " need to be root). This will prompt for path." + @echo "make example Build example programs in \"./build/example/\" and" + @echo " Doxygen documentation in \"./codedoc/example/\"." + @echo "make test Build test programs in \"./build/test/\" and" + @echo " Doxygen documentation in \"./codedoc/test/\"." + exit 0 + +force_: + +install: + ./bin/install + +example: force_ + make -f build/Makefile.example + +test: force_ + make -f build/Makefile.test + diff --git a/README.txt b/README.txt new file mode 100755 index 00000000..ded795a6 --- /dev/null +++ b/README.txt @@ -0,0 +1,24 @@ +Design By Contract for C++ (DBC++) +================================== + +The DBC++ library supports Design By Contract (DBC) for the C++ programming +language. + +Essentially, all Eiffel programming language DBC features are supported by +DBC++. Among others: +1) "old" prefix in postconditions; +2) Subcontracting; +3) Optional contract compilation; +4) Full integration with Doxygen. + +IMPORTANT: The library is still under development, its API should be + considered experimental and subject to change. + +The documentation is yet to be written. Please look at the examples in the +"example/" directory for illustrations on how to use the library. + +Run `make' to see how to build examples, test, install, etc. + +The library requires the C++ Boost library http://www.boost.org/. In particular +Boost.Preprocessor and Boost.MPL are required. + diff --git a/RELEASE.txt b/RELEASE.txt new file mode 100755 index 00000000..31fd488b --- /dev/null +++ b/RELEASE.txt @@ -0,0 +1,19 @@ +RELEASE HISTORY +=============== + +From current to oldest release. +Release_Number ::= Major_Revision.Minor_Revision.SVN_Revision + + +Release 0.1.55 (2009-04-19) +--------------------------- + +Reorganized files to cleanup root directory. +Added installation program. + + +Release 0.1.50 (2009-04-19) +--------------------------- + +First public release. + diff --git a/TODO.txt b/TODO.txt new file mode 100755 index 00000000..707efdd0 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,82 @@ + +/** @todo[LC] Add tests to verify that const mem fun body cannot change obj +state (compiler error) while non-const mem fun body can. */ + +/** @todo[LC] Make sure DBC library complies with STL exceptions safety rules +(detructors don't throw, etc) -- read from C++ book. There is also a discussion +of exception safety and invariant checking in Str97! */ + +/** @todo[LC] Shall self be available in constructor preconditions? +Maybe not because technicall there's no object before exec of constrcutor body +(and for example inv was not verified yet so using self of an obj on which inv +is not checked could lead to strange errors and contract violation). However, +then constrcutor preconditions will not be able to call mem functions +(using self.some_function())... is this too much of a limitation to enforce? +What does Eiffel do? Can you call object features from object's constructor +preconditions? */ + +/** @todo[LC] Does "virtual inline" mean anything? Check in Str97 and remove it +from fun.hpp in case it does not mean anything... */ + +/** @todo[LC] I have seen GCC internal segfaul error if DBC_CONFIG_MAX_ARGC is +more than 3 some some of the DBC++ test programs... I wonder is the compiler +gets confused by fun<...> instead of fun0, fun1, ... (maybe the template +specialization with partial default argument is not properly supported...). */ + +/** @todo[LC] Add all @file w/ copyright info -- see what Boost copyright text +says... Add reference to book from which examples where taken to all example +doc comments. */ + +/** @todo[LC] Create a test for copyable where both class and args are declared +copyable and not. */ + +/** @todo[LC] Write a test for required(). */ + +/** @todo[LC] When invoking parent's function directly from overriden function, +must use DBC_BODY() to avoid infinite recursive checking of contract. See +NameList example. +@code + struct B DBC_INHERIT_OBJECT(B) { + virtual void f() DBC_MEM_FUN(..., {}) + ... + }; + struct D: public B DBC_TRAILING_OJECT(D) { + virtual void f() DBC_MEM_FUN(..., { B::DBC_BODY(f)(); }) + // This instead, will cause infinite contract checking recursion. + virtual void f() DBC_MEM_FUN(..., { B::f(); }) + ... + }; +#endcode +This is quite annoying as a small developper error in omitting DBC_BODY() in +this context will result in a infinite recursion a run-time!!! Can I avoid this +somehow??? Can I check it a compile-time? Can I make it work even if calling +w/out DBC_BODY()? Also, what happens if the body function is not declared +virtual but only the actual function is declared virtual? +More in general than above todo, how shall DbC handle recursion? +Shall contract checking always be disabled when a function recursively invokes +itself? Maybe not, since it might invoke itself w/ different arguments and w/ +obj in a different state (is it is not a const mem fun)... What is Eiffel +policy on DbC and recursion? */ + +/** @todo[LC] When constrcutor contract is delegated to init() initializer, the +contract does not show up in Doxygen... that is because constr (which are +public) have no contract, while init() has the contract but it is private... +is there a way around this? Since Doxygen reports the header files, the contract +code (for init() also) is in the reported header but that's not ideal... It'd be +nice if I could say in the Doxyfile to report init() (and del()) doc even if +they are private... */ + +/** @todo[LC] Make library thread-safe if DBC_THREADING is #define. +In this case: DbC++ will also require Boost.Threading. sync_<> must have mutex. +dbc_global_checking_ must have a mutex. Exectuiong inv+require+body+inv+ensure +must be atomic (sync'd by a mutex) so that effectivelly only 1 operation at the +time for class can be exec among the operations w/ contracts. Consider impl +waiting pre/post conditions. What would this do to performances? This will add +quite a bit on sync at the class level but also at a global level via +dbc_global_checking_... */ + +/** @todo[LC] Can I use Boost.MPL none and/or void to get rid of dbc::none_? */ + +/** @todo[LC] Fix all Boost.MPL static assertions in dbc::fun<>. Do I need to +add any more static assertions? */ + diff --git a/bin/README.txt b/bin/README.txt new file mode 100755 index 00000000..2de63e2a --- /dev/null +++ b/bin/README.txt @@ -0,0 +1 @@ +Binary files, programs, and scripts. diff --git a/bin/install b/bin/install new file mode 100755 index 00000000..7031fdb4 --- /dev/null +++ b/bin/install @@ -0,0 +1,21 @@ +#!/bin/bash + +echo "Design By Contract for C++ (DBC++) Library Installation" + +dest="" +echo +echo -en "Enter C/C++ header files directory [/use/include/]: " +read dest +if [ -z $dest ]; then dest="/usr/include/"; fi + +mkdir -p $dest && cp -R include/dbc* $dest +ret=$? +echo +if [ $ret -ne 0 ]; then + echo "ERROR: Unable to copy DBC++ header files to \"$dest\"." + echo "Do you need to be root to access this directory?" +else + echo "DBC++ header files successfully copied to \"$dest\"." + echo "Make sure this directory is in your C/C++ headers path (-I compiler option) and you are ready to use DBC++." +fi + diff --git a/bin/must-fail b/bin/must-fail new file mode 100755 index 00000000..9282fe7c --- /dev/null +++ b/bin/must-fail @@ -0,0 +1,19 @@ +#!/bin/bash + +# Execute program given at command line. +$@ + +# If program did not fail, return error code 1. +if [ $? -eq 0 ]; then + echo + echo "must-fail: error: executed program did not fail as expected" + echo + exit 1; +fi + +# Else (program failed), return OK code 0. +echo +echo "must-fail: executed program failed as expected -- returning OK" +echo +exit 0 + diff --git a/build/Makefile.example b/build/Makefile.example new file mode 100755 index 00000000..ec564ee5 --- /dev/null +++ b/build/Makefile.example @@ -0,0 +1,80 @@ +# $Id$ # + +def:=-DDBC_ALL -DDBC_CONFIG_LOG_LEVEL=DBC_LOG_LEVEL_ALL + +inc:=-I./include +src:=./example +build:=./build/example +codedoc:=./codedoc/example + + +all: DBC_by_Example OO_SW_Construction CPP_Prog_Lang doc + +clean: + rm -rf build + +force_: + + +DBC_by_Example: CustomerManager Stack Dictionary SimpleQueue Courier NameList Observer Counter + +CustomerManager: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/DBC_by_Example/CustomerManager/CustomerManager.cpp $(src)/DBC_by_Example/CustomerManager/main.cpp -o $(build)/CustomerManager + +Stack: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/DBC_by_Example/Stack/main.cpp -o $(build)/Stack + +Dictionary: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/DBC_by_Example/Dictionary/main.cpp -o $(build)/Dictionary + +SimpleQueue: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/DBC_by_Example/SimpleQueue/main.cpp -o $(build)/SimpleQueue + +Courier: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/DBC_by_Example/Courier/couriers.cpp $(src)/DBC_by_Example/Courier/main.cpp -o $(build)/Courier + +NameList: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/DBC_by_Example/NameList/names.cpp $(src)/DBC_by_Example/NameList/main.cpp -o $(build)/NameList + +Observer: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/DBC_by_Example/Observer/main.cpp -o $(build)/Observer + +Counter: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/DBC_by_Example/Counter/main.cpp -o $(build)/Counter + + +OO_SW_Construction: Gcd Stack4 Stack3 + +Gcd: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/OO_SW_Construction/Gcd/main.cpp -o $(build)/Gcd + +Stack4: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/OO_SW_Construction/Stack4/main.cpp -o $(build)/Stack4 + +Stack3: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/OO_SW_Construction/Stack3/main.cpp -o $(build)/Stack3 + + +CPP_Prog_Lang: String + +String: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/CPP_Prog_Lang/String/main.cpp -o $(build)/String + + +doc: force_ + mkdir -p $(codedoc) + doxygen codedoc/Doxyfile.example + + diff --git a/build/Makefile.test b/build/Makefile.test new file mode 100755 index 00000000..282605c3 --- /dev/null +++ b/build/Makefile.test @@ -0,0 +1,91 @@ +# $Id$ # + +def:=-DDBC_ALL -DDBC_CONFIG_LOG_LEVEL=DBC_LOG_LEVEL_ALL + +inc:=-I./include +src:=./test +build:=./build/test +bin:=./bin +codedoc:=./codedoc/test + + +all: Construction Subcontracting Operators Checking doc + +clean: + rm -rf $(build) + +force_: + + +Construction: construction + +construction: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Construction/construction.cpp -o $(build)/construction + + +Subcontracting: base_class base_template class_subcontracts_template template_subcontracts_template + +base_class: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Subcontracting/base_class.cpp -o $(build)/base_class + +class_subcontracts_class: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Subcontracting/class_subcontracts_class.cpp -o $(build)/class_subcontracts_class + +template_subcontracts_class: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Subcontracting/template_subcontracts_class.cpp -o $(build)/template_subcontracts_class + +base_template: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Subcontracting/base_template.cpp -o $(build)/base_template + +class_subcontracts_template: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Subcontracting/class_subcontracts_template.cpp -o $(build)/class_subcontracts_template + +template_subcontracts_template: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Subcontracting/template_subcontracts_template.cpp -o $(build)/template_subcontracts_template + + +Operators: operators + +operators: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Operators/operators.cpp -o $(build)/operators + + +Checking: checking + +checking: + mkdir -p $(build) + # Compile this test with different DBC_CHECKING... levels. + # Verbose log DBC_CONFIG_LOG_LEVEL set by program code itself. + g++ -Wall $(inc) -DDBC_NO $(src)/Checking/checking.cpp -o $(build)/checking-no + g++ -Wall $(inc) -DDBC_ALL $(src)/Checking/checking.cpp -o $(build)/checking-all + # Only one at the time + g++ -Wall $(inc) -DDBC_CHECK_REQUIRE $(src)/Checking/checking.cpp -o $(build)/checking-require_only + g++ -Wall $(inc) -DDBC_CHECK_ENSURE $(src)/Checking/checking.cpp -o $(build)/checking-ensure_only + g++ -Wall $(inc) -DDBC_CHECK_INVARIANT $(src)/Checking/checking.cpp -o $(build)/checking-invariant_only + # Eiffel like combinations. + g++ -Wall $(inc) -DDBC_CHECK_REQUIRE $(src)/Checking/checking.cpp -o $(build)/checking-require + g++ -Wall $(inc) -DDBC_CHECK_REQUIRE -DDBC_CHECK_ENSURE $(src)/Checking/checking.cpp -o $(build)/checking-require_and_ensure + g++ -Wall $(inc) -DDBC_CHECK_REQUIRE -DDBC_CHECK_ENSURE -DDBC_CHECK_INVARIANT $(src)/Checking/checking.cpp -o $(build)/checking-require_ensure_and_invariant + # Code documentation. + g++ -Wall $(inc) -DDBC_DOC $(src)/Checking/checking.cpp -o $(build)/checking-doc + # Invalid combinations -- compilation must fail. + $(bin)/must-fail g++ -Wall $(inc) -DDBC $(src)/Checking/checking.cpp -o $(build)/checking-dbc + $(bin)/must-fail g++ -Wall $(inc) -DDBC_NO -DDBC_ALL $(src)/Checking/checking.cpp -o $(build)/checking-no_and_all + $(bin)/must-fail g++ -Wall $(inc) -DDBC_DOC -DDBC_ALL $(src)/Checking/checking.cpp -o $(build)/checking-doc_and_all + + +doc: force_ + mkdir -p $(build) + g++ -Wall $(inc) $(def) $(src)/Doc/doc.cpp -o $(build)/doc + mkdir -p $(codedoc) + doxygen codedoc/Doxyfile.test + + diff --git a/build/REAME.txt b/build/REAME.txt new file mode 100755 index 00000000..14c13150 --- /dev/null +++ b/build/REAME.txt @@ -0,0 +1 @@ +Build configuration and output files. diff --git a/build/svnignore b/build/svnignore new file mode 100755 index 00000000..52782bba --- /dev/null +++ b/build/svnignore @@ -0,0 +1,6 @@ +#!/bin/bash + +svn propset svn:ignore ' +example +test +' . diff --git a/codedoc/Doxyfile.example b/codedoc/Doxyfile.example new file mode 100755 index 00000000..a49077e8 --- /dev/null +++ b/codedoc/Doxyfile.example @@ -0,0 +1,1356 @@ +# Doxyfile 1.5.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./codedoc/example + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ./example + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = ./include + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DBC_DOC + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is enabled by default, which results in a transparent +# background. Warning: Depending on the platform used, enabling this option +# may lead to badly anti-aliased labels on the edges of a graph (i.e. they +# become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/codedoc/Doxyfile.test b/codedoc/Doxyfile.test new file mode 100755 index 00000000..08d81be3 --- /dev/null +++ b/codedoc/Doxyfile.test @@ -0,0 +1,1356 @@ +# Doxyfile 1.5.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./codedoc/test + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ./test/Doc + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = ./include + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DBC_DOC + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is enabled by default, which results in a transparent +# background. Warning: Depending on the platform used, enabling this option +# may lead to badly anti-aliased labels on the edges of a graph (i.e. they +# become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/codedoc/REAME.txt b/codedoc/REAME.txt new file mode 100755 index 00000000..5d354a15 --- /dev/null +++ b/codedoc/REAME.txt @@ -0,0 +1 @@ +Source code documentation (see "doc/" for user's manuals instead). diff --git a/codedoc/svnignore b/codedoc/svnignore new file mode 100755 index 00000000..52782bba --- /dev/null +++ b/codedoc/svnignore @@ -0,0 +1,6 @@ +#!/bin/bash + +svn propset svn:ignore ' +example +test +' . diff --git a/doc/DBC_Documentation.txt b/doc/DBC_Documentation.txt new file mode 100755 index 00000000..90858789 --- /dev/null +++ b/doc/DBC_Documentation.txt @@ -0,0 +1,417 @@ +
+
+Design By Contract (DBC) for C++ (DBC++) Library
+================================================
+
+The DBC++ library supports Design By Contract (DBC) [Mey97] for the C++ 
+programming language [Str97].
+
+Essentially, all Eiffel programming language DBC features are supported by 
+DBC++. Among others:
+1)  "old" prefix in postconditions;
+2)  Subcontracting;
+3)  Optional contract compilation;
+4)  Full integration with Doxygen.
+
+NOTE:   Most of the documentation still needs to be added. For now, you can 
+        take a look at the examples to see how the DBC++ library works.
+
+AUTHOR:     Lorenzo Caminiti, 2009 (lorenzo_caminiti@hotmail.com)
+LICENCE:    The DBC++ library is distributed under the LGPL licence.
+            http://sourceforge.net/projects/dbcpp/
+
+
+1. VERSION
+----------
+
+The current DBC++ version is v0.1 (development).
+
+This version is still an early development version and the library could change
+quite a bit even in its APIs. Furthermore, validation, debugging, and testing
+is happing now. If you see an issue with this library, your are welcome to
+report it; but expect to see issues with this early version.
+
+
+2. EXAMPLES (GETTING STARTED)
+-----------------------------
+
+To compile the examples run `make' in the "example" directory, the binaries 
+will be created in the "example/build" directory.
+
+Examples are mainly taken from [Mey97] and [Mit02]. Start by looking at:
+a)  "example/OO_SW_Construction/Stack4" for a good general example on how to 
+    write contracts.
+b)  "example/DBC_by_Example/Courier" for an example on how to write
+    subcontracts for inherited classes.
+c)  "Other/Simple" for a real basic example of how to use DBC++ without its
+     macro APIs (only if you are interested in exploring how the library
+     actually works, otherwise just use the macro APIs).
+
+
+3. FEATURES
+-----------
+
+This is a list of DBC features and how they are supported by DBC++.
+    DBC FEATURE                                     DBC++ SUPPORT
+    Class invariants                                Yes
+    Contracts for member functions                  Yes
+    Contracts for constructors                      Yes(2)
+    Contracts for destructor                        Yes(3)
+    "old" and "result" in postconditions            Yes(5)
+    Contracts of overloaded member functions        Yes(4)
+    Contracts for derived class (subcontracting)    Yes
+    Contracts for templates class                   Yes
+    Contracts for pure virtual member functions     Yes
+    Contracts for template member functions         Yes(1)
+    Contracts for member operators                  Yes(1)
+    Loop invariants                                 Yes
+    Optional compilation of contracts               Yes
+    Automatic contract documentation (Doxygen)      Yes(7)
+    Limited access in contracts                     No(6)
+    Concurrency (multi-threading)                   Not yet (work in progress)
+
+(1) This is only supported by DBC++ code APIs and not by the macro APIs.
+
+(2) If your constructor implementation uses member initializer syntax to 
+initialize member variable, the constructor implementation needs to be in the 
+header file (not in the .cpp). Usually constructor implementations are short 
+enough that can be put in the header file so this is not an issue. However, a 
+possible way around this is to delegate the constructor implementation to a
+*private* init() member function then write the contract for init() and not for 
+the constructor. Using a "two stage" construction via init() has its drawbacks
+(see [Str97]) so the general reccomandation is to implement the constructors
+in .hpp when using DBC++ unless you were already using "two stage" construction
+regardless of DBC++ (for example to implement delegating constructors.
+
+(3) This feature is implemented as specified in [C++04] (there is no direct
+indication on how DBC should be supported for destructors in [Mey97]).
+
+(4) A limitation is that the overloaded function must use different argument
+names (not just argument types). For example, DBC++ will supports contracts for
+"C::f(const dobule& d)" and "C::f(const int& i)" because they use differnt 
+argument names, "d" and "i", when overloading "f". However, DBC++ will not be 
+able to support contracts for "C::f(const dobule& x)" and "C::f(const int& x)" 
+because both overloaded member functions use the samebargument name "x". This 
+is not an important limitation, just use different argument names.
+
+(5) In postconditions, "result" refers to the returned value -- there is no
+"result" in contracts for "void" member functions. Also, entities (the object,
+named "self", and the arguments) are accessed via: their name ".now", to get
+their value now after the function body has been executed; or their name 
+".old", to get their value before the body was executed. E.g., "self.now"
+refers to the object after the function body execution, "self.old" refers to
+the object before the body execution. The entity is internally copied by DBC++ 
+before the body execution -- be careful, if the entity copy constructor only 
+provides a shallow copy then ".old" might not be what you intended. Because not 
+all entities can be copied in C++ (copy constructor can be made private for non-
+copiable objects), you must explicitly indicate to DBC++ that an entity type 
+"E" is copiable (has a public copy constructor) using "dbc::copiable". This
+will allow you to apply ".old" to such entity. By default entities types are
+considered non-copiable as being copiable adds an extra requirement on the type
+that might not be satisfied by all types (especially if the type is a template
+parameter, it might later be set to a non-copiable type). Therefore the 
+copiability is explicitly required and only when ".old" is needed in your 
+contract. Furthermore, the copy constrcutor might have performance impact. For
+example, if an argument is passed by value or as constant reference, you can 
+safely use ".now" as the body execution will not have been able to change the 
+argument value.
+
+(6) In Eiffel, only public features can be called in preconditions -- because
+the client of the class should correctly be able to check the contract before 
+invoking an operation, see [Mey97]. However, DBC++ is not able to restrict 
+member function or variable access in contracts. Precoditions, postconditions, 
+and invariant can access public, protected, and private parts of the object. It 
+is strongly recommended you write contracts that use public members as much as 
+possible, especially in preconditions -- see good motivations and practices for 
+this in [Mey97] and [Mit02]. Furthermore, in contracts you can only access 
+entities as constant references "const&" so you cannot change them. This is 
+critical as contracts must not alter the state of the object, the operation 
+argument, or result -- do not mess things up using "const_cast\<\>()". Finally, 
+you can write any form of C++ code in DBC++ contracts but limit yourself in 
+writing a list of assertions -- if you write complex code in contracts, you 
+will likely introduce code bugs in the contract itself instead of writing 
+assertions to check that there is no bug in the rest of your software.
+
+(7) The documentation generated by Doxygen will essentially look like the 
+class' "short form" and "flat-short from" defined in [Mey97].
+
+
+4. APIs
+-------
+
+DBC++ provides a macro based APIs. However, you can also write DBC++ contracts
+without using such macro APIs.
+
+Pros (+) and cons (-) of macro APIs:
+(+) Macro APIs hide all the contract setup framework so you only write the
+    preconditions, postconditions, and invariants (as it should be).
+(+) Macro APIs automatically generate Doxygen documentation for the contracts
+    you write.
+(-) You will have to "trust" the macro APIs without really understanding how
+    the DBC++ works behind the scene -- most of the times this should be OK.
+(-) The macro APIs generate a compilation warning for 'g++' as friend class
+    declarations are repeated more than one time -- you can just ignore this
+    warning (this is a very minor cons).
+
+The following is a summary of the macro APIs to write DBC++ contracts. Refer to
+the code in the "example" directory to see how these APIs are used.
+
+Class invariant.
+    DBC_INVARIANT(full_class_type, invariant_code)
+
+Constructor.
+    DBC_CONSTRUCTORn(access, class_type,
+            [arg1_type, arg1, arg2_type, arg2, ...],
+            require_code, ensure_code, body_code)
+Constructor of template class.
+    DBC_TEMPLATE_CONTRUCTORn(/* same as above */)
+            
+Destructor.
+    DBC_DESTRUCTOR(access, virtual_mem_fun], class_type, body_code)
+    
+Non-void member function.
+    DBC_MEM_FUNn(access, virtual_mem_fun, return_type, class_type, 
+            mem_fun, [arg1_type, arg1, arg2_type, arg2, ...], const_mem_fun,
+            require_code, ensure_code, body_code)
+Void member function.
+    DBC_MEM_VOIDFUNn(/* same as above */)
+Non-void member function of template class.
+    DBC_TEMPLATE_MEM_FUNn(/* same as above */
+Void member function of template class.
+    DBC_TEMPLATE_MEM_VOIDFUNn(/* same as above */)
+    
+Derived non-void member function.
+    DBC_DERIVED_MEM_FUNn(access, virtual_mem_fun, return_type, class_type,
+            mem_fun, [arg1_type, arg1, arg2_type, arg2, ...], const_mem_fun,
+            base_class_type, /* new argument */
+            require_code, ensure_code, body_code)
+Derived void member function.
+    DBC_DERIVED_MEM_VOIDFUNn(/* same as above */)
+Derived non-void member function of template class.
+    DBC_DERIVED_TEMPLATE_MEM_FUNn(/* same as above */
+Derived void member function of template class.
+    DBC_DERIVED_TEMPLATE_MEM_VOIDFUNn(/* same as above */)
+
+Member function name for implementation in .cpp file.
+    DBC_BODY(mem_fun)
+
+Parameters:
+    "n" postfix     Number of arguments. For example, for a constructor 
+                    with 2 arguments, use the 'DBC_CONSTRUCTOR2()' macro. The
+                    current limits for n are [0, 3]. It is trivial to increase
+                    this limit and later versions of DBC++ will be extended to
+                    support at least n in [0, 10] -- 10 arguments.
+    access          Access keyword 'public', 'protected', or 'private'.
+    virtual_mem_fun Keyword 'virtual' if virtual member function. Leave empty
+                    otherwise.
+    return_type     Return type name. 'void' if void member function.
+    class_type      Class type name (do not specify namespace, template
+                    parameters, etc, just class type name itself).
+    full_class_type Fully qualified class type name (with namespace prefix,
+                    template parameters postfix*, etc).
+    mem_fun         Member function name.
+    argI_type       Type of argument I-th, for I in [1, n] (not there if n=0).
+    argI            Name of argument I-th, for I in [1, n] (not there if n=0).
+    const_mem_fun   Keyword 'const' if constant member function. Leave empty
+                    otherwise.
+    base_class_type Type name of base class from which to inherit contract (for
+                    subcontracting only).
+    require_code    Preconditions code block "{ ... }".
+    ensure_code     Postconditions code block "{ ... }".
+    body_code       Member function implementation code block "{ ... }". Use
+                    ";" if implementation will instead appear in .cpp file. Use
+                    " = 0;" for pure virtual member functions.
+    invariant_code  Class-invariants code block "{ ... }".
+
+(*) The C++ preprocessor cannot parse templates with more than 1 parameter as a
+    valid macro parameter. Therefore, if your class is a template with more
+    than 1 argument, say 'my_namespace::my_class', you must pass it via
+    an addional macro definition:
+        #define _MY_CLASS_TYPE my_namesoace::my_class
+        DBC_INVARIANT(_MY_CLASS_TYPE, { /* your invariant assertions */ })
+
+
+5. ASSERTION
+------------
+
+There are two assertion classes to support writing assertion in preconditions,
+postconditions, and class invariant code blocks: 'dbc::assertion' and
+'dbc::oassertionstream'.
+
+These macros allow to check assertions using the two assertion classes.
+    DBC_ASSERT(condition, label)
+    DBC_ASSERT_STREAM(condition, label, stream_label_code)
+    
+Parameters:
+    condition   Assertion boolean condition. An exception will the thrown if
+                this condition is false.
+    label       C-style string to name the assertion. This label is part of the
+                Doxygen documentation.
+    stream_label_code   Provides a verbose description of the assertion 
+                condition possibly with variables value, etc (for assertion 
+                stream only). The DBC_ASSERT_STREAM declares a local variable
+                'astream' of type 'dbc::oassertstream'. This label code can be
+                of the form 'astream << "result " << result << " must be true"'
+                to write in the assertion stream a description of the assertion
+                that is more verbose of its "label". This will provide more 
+                information in case the assertion fails. (This code is only
+                executed in case the condition if false or on verbose DBC++ log
+                level to avoid evaluating expensive I/O under normal 
+                circumstances.) If this code ends with "<< dbc::raise()"
+                then an instance of the exception type E is thrown in case of
+                condition violation. You can use this to throw your own 
+                exceptions instead of the default ones thrown by DBC++ (
+                'dbc::precondition_violation', 'dbc::postcondition_violation',
+                and 'dbc::invariant_violation'). The default exceptions thrown
+                by DBC++ are derived from 'std::logic_error'. The custom 
+                exception E you can throw using '<< dbc::raise()' must have
+                a constructor accepting a C-style null-terminated string that
+                will be set by DBC++ to the assertion stream label.
+                
+
+6. CONTRACTS IN HEADER FILES
+----------------------------
+
+Contracts are written in the header file (.hpp) together with the code
+specification (declaration). Implementation (definition) of the class can either
+go in the header file of in the code file (.cpp).
+
+It is strongly recommended to put the implementation in the .cpp file so it is
+separate from the specifications -- see [Str97]. Contracts are part of the 
+specifications so they correctly appear in the header file.
+(Templates are usually a known exception to this rule in C++ as in most cases
+their implementation will have to leave in the header file -- this depends on 
+you compiler and build system -- but you can still split the template 
+specification at the top of the header file, and its implementation at bottom.)
+
+
+7. COMPILATION OPTIONS
+----------------------
+
+A few macro symbols are used to control when contracts are compiled in the 
+object code, what parts of the contract' assertions are checked at run-time, 
+and to configure other DBC++ behaviours.
+
+DBC++ will use default #definition of the following symbols unless you #define
+them *before* the 1st #inclusion of the "dbc.hpp" header file. To make sure you
+#define these symbols before each #inclusion, it is recommended you #include 
+"dbc.hpp" in one header only, say "mydbc.hpp", and have any other file of your 
+system include this one header. (Another option would be to #define these
+symbols using your build system.)
+
+    -- File: mydbc.hpp --
+    // #define DBC configuration symbols as needed.
+    #include  // Single place that actually #includes "dbc.hpp"
+
+
+    -- File: myprogram1.cpp --
+    #include "mydbc.hpp"
+    // Your code...
+
+    -- File: myprogram1.cpp --
+    #include "mydbc.hpp"
+    // Your code...
+
+The following macro symbols control optional contract compilation and run-time
+checking (this is similar to what supported by Eiffel [Mey97]).
+    MACRO SYMBOL            ACTION IF #DEFINED
+    DBC_NO                  All symbols below are #undefined.
+    DBC                     Do not compile (and do not check) any DBC++ code.
+                            Any DBC++ contract code will be *completely*
+                            stripped out of your object code.
+    DBC_CHECK_REQUIRE       Compile and check preconditions.
+    DBC_CHECK_ENSURE        Compile and check postconditions.
+    DBC_CHECK_INVARIANT     Compile and check class invariants.
+    DBC_CHECK_LOOP          Compile and check loop invariants.
+    DBC_ALL                 All symbols above are #defined.
+By #defining some of these symbols when generating object code from one class
+and a #defining a different set of symbols when compiling a different class,
+you can compile and check different contract parts for different components of
+your system (this will complicate a bit your build system).
+
+The following macro symbols can be used to customize some DBC++ behaviour.
+    DBC_CONF_LOG_LEVEL
+        Indicate that log messages are printed (if a message is not printed, it
+        is not generated at all so to save a bit in performance).
+            dbc::LOG_LEVEL_NONE         none
+            dbc::LOG_LEVEL_ASSERTION    assertion messages only
+            dbc::LOG_LEVEL_TRACE        assertion and trace messages
+            dbc::LOG_LEVEL_ALL          all the abvoe
+        Default definition: dbc::LOG_LEVEL_ALL
+    DBC_CONF_LOG_TRACE(message)
+        Print specified trace message (null-terminated C-style string).
+        Default definition: '{ std::clog << "dbc: " << message << std::endl; }'
+    DBC_CONF_LOG_ASSERTION(message)
+        Print specified assertion message (null-terminated C-style string).
+        Default definition: '{ std::cerr << "dbc: " << message << std::endl; }'
+    DBC_CONF_DOC_ASSERTION(condition, label)
+        Generate documentation comments for assertion with specified condition
+        and label. Default definition generates Doxygen comments.
+    DBC_CONF_DOC_PRE(require_code)
+        Generate documentation comments for preconditions of the specified
+        require_code code block. Default definition generates Doxygen comments.
+    DBC_CONF_DOC_POST(ensure_code)
+        Generate documentation comments for postconditions of the specified
+        ensure_code code block. Default definition generates Doxygen comments.
+    DBC_CONF_DOC_INVARIANT(full_class_type, invariant_code)
+        Generate documentation comments for class-invariants of the specified
+        class type name (fully qualified with namespace prefix, template
+        parameters, etc) and invariant_code code block. Default definition
+        generates Doxygen comments.
+        NOTE: Beacuse C++ cannot parse template as macro parametrs and
+        full_class_type in general can be a template, DBC++ must internally
+        wrap the calss type within parenthesis when passing it as
+        full_class_type. For example, for a fully qualified class template type
+        'my_namespace::my_class', 'full_class_type' will be
+        '(my_namespace::my_class)' (note the extra parenthesis wrapping
+        the actual type). You documentation tool must be able to deal with this
+        (e.g., Doxygen ignores the parenthesis wrapping when referring to the
+        type).
+
+
+8. CONTRACT DOCUMENTATION
+-------------------------
+
+DBC++ automatically documents your contracts using Doxygen. Run `doxygen' in 
+the "example" directory to generate the documentation for all the examples in 
+the "example/codedoc" directory.
+
+In [Mey97] the "short form" of a class is essentially defined as the 
+specification of its public entities (signature and contract) and the related 
+documentation. If you instruct Doxygen to ignore private entities (option in 
+Doxyfile), the documentation generated by Doxygen can effectively be considered 
+a "short form" of your class with the contract documentation atuomatically
+added by DBC++. Furthermore, Doxygen will link a derived class and its 
+inherited member functions to its base, this will effectively allow your to
+browse the documentation like the "flat-short form" defined in [Mey97].
+
+In the documentation generated by Doxygen, you will see class invariant, 
+operation post- and pre- documented from the contract source code in Doxygen
+"codedoc/html/index.html". The following configuration is required in the
+Doxyfile to support DBC++ (see "example/Doxyfile").
+
+    -- File: Doxyfile --
+    ...
+    MACRO_EXPANSION = YES        # Allows for exaping DBC++ macros.
+    INCLUDE_PATH    = ../include # Path to "dbc.hpp" to resolve DBC++ macros.
+    PREDEFINED      = DBC_DOC    # Instruct DBC++ to generate assertion doc.
+
+If you want to use a code documentation tools different from Doxygen, you
+should be able to integrate with DBC++ by #defining the macros prefixed by 
+DBC_CONF_DOC_.
+
+
+9. REFERENCES
+-------------
+
+In reference symbol alphabetic order.
+[C++04] Proposal to add Design by Contract to C++. 
+        Doc. no. N1613=04-0053, 2004.
+[Mey97] B. Meyer. Object Oriented Software Construction, 2nd Edition. 
+        Prentice-Hall, 1997.
+[Mit02] R. Mitchell, J. McKim. Design by Contract, by Example.
+        Addison Wesley, 2002.
+[Str97] B. Stroustrup. The C++ Programming Language, Special Edition. 
+        Addison Wesley, 1997.
+
+
diff --git a/doc/ERRORS.txt b/doc/ERRORS.txt new file mode 100755 index 00000000..5a57caac --- /dev/null +++ b/doc/ERRORS.txt @@ -0,0 +1,47 @@ + +List of errors (as generated by GCC 4.2.4) and their common causes. + +error: ‘mem’ is not a template + Make sure class_type is specified to be a template. + MACRO API: (template)(class_type) + CODE API: dbc::fun<>::mem::template class_type + +error: invalid use of non-static data member ‘...’ + Did you forget to use self? + +error: no match for ‘...’ in ‘...’ + Did you forget .now/.old postfix in postconditions? You must use these + postfix in precondition for self as well as all function argument names + (but not for 'result') + +error: ‘...’ has no member named ‘old’ + Are you using old on either the class or on of the member function + arguments without qualifing it with DBC_COPYABLE() (or dbc::copyable<>) + first in the contract signature? + +error: macro "DBC_..." passed ... arguments, but takes just ... + Does one or more passed macro parameter contain a comma "," not wrapped in + parenthesis "()"? If so, wrap this macro parameter with DBC_MPARAM(). (This + is the case for templates with more than 1 argument and for code blocks + containing "," when they are passed as macro parameters.) + +warning: ‘class dbc::fun<...>...’ is already a friend of ‘...’ + Ignore this. This is due to macro APIs automatically specifying same + contract type as friend more than one time when you use DBC_MEM_FUN() + multiple times for different member functions. It would be nice if your + compiler could be configured to not generate just this one warning. This is + the ONLY warning that the DBC++ library generates. + +error: ‘dbc_invariant_’ was not declared in this scope + Did you forget to add DBC_INVARIANT(...) at the end of the class? + +Compile-time errors resulting from DBC static assertions (checks). + +error: ...DBC_ERROR_class_must_privately_inherit_from_dbc_object_... + Did you forget to inherit (privately) from dbc::object<>? (Use + DBC_INHERIT_OBJECT() or DBC_TRAILING_OBJECT() macros.) + +error: ...DBC_ERROR_missing_function_arguments_... + Did you forget to specify the function arguments when using the macro APIs? + Use "()" for no argument -- e.g., DBC_MEM_FUN(... (fun-name)() ...). + diff --git a/doc/NOTES.txt b/doc/NOTES.txt new file mode 100755 index 00000000..df8cd7e1 --- /dev/null +++ b/doc/NOTES.txt @@ -0,0 +1,109 @@ + +/** @note boost::mpl::is_copyable<> could not be used to replace +dbc::copyable<> because Boost's is_copyable<> only works for built-in types. +For user defined types, Boost's is_copyable always returns false unless special +compiler support is provided (and GCC does not provide support for this, +probably only VC++ supports this at the moment...). */ + +/** @note Inheriting from a dbc::object<> base class allows to provide +assertion checking state, mutex, etc. However, the invariant cannot be made +pure virtual function of dbc::object<> otherwise the invariant call will always +be ambiguis when subcontracting -- I did try this! +Note, since dbc::object<> is a template, they are all different from eavery +class type so the inheritance tree is modified by no common anchestor is +intorduced and dbc::object<> should be private base anyway -- so this +modification of inheritance tree is OK as is does not change public APIs. +However, contract cannot access class-type directly from dbc::object because +in order to call dbc::object::class_type, the contract will still need to +specify C -- so class-type must still be specified for each contract mem fun. */ + +/** @note Wanted to provide def constr for all contract class fun, fun::mem, etc +so they are easier to use. In particular if a mem var of a contract class type +is declared, C++ will init automatically using it's default constr. This feature +is currently not used in the library (as contracts are init manually in the +fun declaration that then call operator()) but it might turn out to be handy in +future. */ + +/** @note If only DBC_CHECK_INVARIANT and DBC_CHECK_REQUIRE are #define, DbC++ +can be used to impl invariant checking framework similar to the only proposed +in Str97 (see example/CPP_Prog_Lang/String). In Str97, precondition checking is +not directly discussed (it is actually discouraged in a later section) but +String::operator[] has a check on the range of its index argument that is very +naturally handled with a precondition -- so DbC++ example have implemented that +index range check as a precondition. */ + +/** @note declaring require(), ensure(), and dbc_invariant_() pure virtual +forces the user to define them correctly at compile time. Having them with +params allow user to only use correct param (no old or result in require()) +and to name args as original function args. */ + +/** @note Chosen abbreviation "mem" for member and "fun" for function to follow +C++ STL conventions of std::mem_fun. Chosen "constr" and "destr" +abbreviations trying to follow similar abbreviation philosofy of STL. */ + +/** @note DBC_CONFIG_ macros follow name convention and semantics of +BOOST_PP_CONFIG_ macros. */ + +/** @note Contracts are functor as they provide call operator() that checks +contract and inokes body (this seemed a natural choise since this is indeed +the function call). */ + +/** @note DBC_CONFIG_ENABLE_XYZ_ASSERTIONS do not remove assertions from obj +code. They only run on/off run-time checking of relative assertions (but the +assertions are always compiled). Use DBC_CHECK_XYZ to remove pre/post/inv +assertions (at this regardless of if they throw/exit/terminate) from obj +code. */ + +/** @note DBC_NO and DBC_ALL override #definitions of single DBC_CEHCKING (if +any). */ + +/** @note DbC++ also impl a simple version of Eiffel loop invariant checking +(see example/OO_SW_Contruction/Gcd). This hasn't been tested much... */ + +/** @note Different stylistic naming convetions have been used: C/C++ STL for +DBC library and test, Str97 for Str97 examples, Java-like CaMeL for DBC books +examples. This was to see how DBC will look when in code with different +styles (common C++ situation). */ + +/** @note DBC_CONFIG_MAX_ARGC is set to 5 by default. It can be increased +(probably up to BOOST_PP_MAX_MAG around 255) but it will significantly increase +compilation time. Use only a reasonable number based on the number of fun args +you have (for which you need a contract). */ + +/** @note See examples: Stack4 for general good DBC and library usage; NameList +for inheritance; Observer for more complex inheritance and library usage (where +some classes have contracts and others don't -- but I prefer to have contracts +for all classes and all of their mem-fun/constr/destr). */ + +/** @note If when subcontracting DBC_BASE is omitted then base contract is +ignored. This might be inline with C++ "freedom" given to the programmer, but +in general you must NOT do that... as you will be braking your parent's contract +by not inheriting it. */ + +/** @note Do not enter arg default value in contract (only arg-type and +arg-name). */ + +/** @note require/ensure declared pure virtual (instead of {}) so compiler error +if you miss type them (e.g., incorrect const or post<> for one of the args). +This requires a bit more work from the programmer even for contracts with no +pre/post conditions but allow to catch errors in the contract at +compile-time. */ + +/** @note In quite a few places in the impl (macro, fun, etc) we need to +add and them remove copyable etc because if types do not have excaly the same +signature the compiler will not match them to be the same for templates (as for +templates compiler need to wait and eval the type expression lazily...). */ + +/** @note Argument types are indexed from 0 to n-1 like in Boost.FunctionType +they are returned as Boost.MPL sequence that goes from 0 to n-1. Using symbols +indexed from 1 to n (A1...An, arg1_type...argn_type, etc) would have increased +(but not much) preprocessing time because Boost.Preprocessor enum and +repetition go from 0 to n-1 by default. */ + +/** @note Destructor do not subcontract (i.e., their contract do not accept +DBC_BASE() and fun<>::destr<> does not accept base contract type B param). This +is beacuse C++ will automatically invoke base class destructors (with its +contract that cheks inv if base class destructor has a contract). See the +Courier example: When DifferentCourier is destroied, the base class Courier +destructor is automatically invoked. */ + diff --git a/doc/README.txt b/doc/README.txt new file mode 100755 index 00000000..c3b3f537 --- /dev/null +++ b/doc/README.txt @@ -0,0 +1 @@ +User's manuals (see "codedoc/" for code documentation). diff --git a/example/CPP_Prog_Lang/README.txt b/example/CPP_Prog_Lang/README.txt new file mode 100755 index 00000000..19f1033e --- /dev/null +++ b/example/CPP_Prog_Lang/README.txt @@ -0,0 +1 @@ +Implemented some examples from book "The C++ Programming Language", Bjarne Stroustrup. diff --git a/example/CPP_Prog_Lang/String/String.hpp b/example/CPP_Prog_Lang/String/String.hpp new file mode 100755 index 00000000..6ca223b5 --- /dev/null +++ b/example/CPP_Prog_Lang/String/String.hpp @@ -0,0 +1,102 @@ + +#ifndef STRING_HPP_ +#define STRING_HPP_ + +// Must setup #defines before #including dbc.hpp. +#undef DBC_NO +#undef DBC_ALL +#undef DBC_CHECK_REQUIRE +#undef DBC_CHECK_ENSURE +#undef DBC_CHECK_INVARIANT +#undef DBC_CHECK_LOOP +// Make sure ONLY invariants and preconditions are checked. +#define DBC_CHECK_REQUIRE +#define DBC_CHECK_INVARIANT + +#include +#include +#include + +/** + * Example of invariant (and simple precondition) checking from [Str97]. + * In original example String from [Str97] only the implementation of + * operator[] and size() were provided. Therefore, from that example it was + * clear that operator[] was checking the invariant while size() was not. + * However, it was not indicaed if constrcutor and destructor were to check the + * invaraint or not. I have implemented constructor and destructor here so they + * check the invariant also (remove contract from delegated contrcuting + * function init() and destructor to prevent them from checking the invariant). + * [Str97] "The C++ Programming Language", B. Stroustrup, 1997. + */ +/** @todo[LC] Read about inv and exception from [Str97]. */ +class String DBC_INHERIT_OBJECT(String) { +public: + /** Thrown when out of range indexspecified (precondition failure). */ + class Range {}; + /** Thrown on invarint failure. */ + class Invariant {}; + + /** Max limit on string length. */ + enum { TOO_LARGE = 16000 }; + + /** Construct from C-style null-terminated string. */ + String(const char* q): sz(), p() { init(std::string(q)); } + + /** Construct from C++ string. */ + String(const std::string& s): sz(), p() { init(s); } + + /** Destroy. */ + ~String() + DBC_DESTRUCTOR( (public) (String)(), { + delete[] p; + }) + + /** Returns character at specified index. */ + char& operator[](int i) + DBC_MEM_FUN( (public) (char&) (String) (operator_at)( (int)(i) ), { + DBC_ASSERT_STREAM(i >= 0 && i < self.sz, "i in range", + err << dbc::uraise(Range())); + }, { + }, { + return p[i]; + }) + + /** Returns size (total number of characters). */ + int size() const + DBC_MEM_FUN( (public) (int) (String) (size)() (const), { + }, { + }, { + return sz; + }) + +private: + /** Internal string size. */ + int sz; + /** Internal string representation. */ + char* p; + + /** Delegating initializator (call only from constructors). */ + void init(const std::string& s) + DBC_CONSTRUCTOR( (public) (String)( (const std::string&)(s) ), { + }, { + }, { + sz = s.size(); + p = new char[sz + 1]; + strcpy(p, s.c_str()); + }) + + DBC_INVARIANT(String, { + DBC_ASSERT_STREAM(self.p != 0, "p exists", + err << dbc::uraise(Invariant())); + DBC_ASSERT_STREAM(self.sz >= 0 && self.sz < String::TOO_LARGE, + "size in range", err << "size " << self.sz + << " out of range [0, " << String::TOO_LARGE << ")" + << dbc::uraise(Invariant())); + // We can safely access p[sz] here bacause above assertions passed. + DBC_ASSERT_STREAM(!self.p[self.sz], "p null-terminated", + err << dbc::uraise(Invariant())); + }) +}; + +#endif // STRING_HPP_ + diff --git a/example/CPP_Prog_Lang/String/main.cpp b/example/CPP_Prog_Lang/String/main.cpp new file mode 100755 index 00000000..9072bd90 --- /dev/null +++ b/example/CPP_Prog_Lang/String/main.cpp @@ -0,0 +1,15 @@ + +#include "String.hpp" +#include + +int main() { + std::cout << std::endl << "init()..." << std::endl; + String s("Galileo Galilei"); + + std::cout << std::endl << "operator[](0)..." << std::endl; + std::cout << s[0] << std::endl; + + std::cout << std::endl << "del()..." << std::endl; + return 0; +} + diff --git a/example/DBC_by_Example/Counter/Counter.hpp b/example/DBC_by_Example/Counter/Counter.hpp new file mode 100755 index 00000000..9246b7e8 --- /dev/null +++ b/example/DBC_by_Example/Counter/Counter.hpp @@ -0,0 +1,55 @@ +/* $Id$ */ + +#ifndef COUNTER_HPP_ +#define COUNTER_HPP_ + +#include "../Observer/Subject.hpp" +#include + +/** Positive integer counter. */ +class Counter: public Subject DBC_TRAILING_OBJECT(Counter) { +public: + // Creation // + + /** Construct counter with specified initial value. */ + Counter(const int& value = 10): value_(value) + DBC_CONSTRUCTOR( (public) (Counter)( (const int&)(value) ), { + }, { + }, {}) + + /** Destroy counter. */ + virtual ~Counter() + DBC_DESTRUCTOR( (public) (virtual) (Counter)(), {}) + + // Queries // + + /** Return counter value. */ + int value() const + DBC_MEM_FUN( (public) (int) (Counter) (value)() (const), { + }, { + }, { + return value_; + }) + + // Commands // + + /** Decrement counter's value. */ + void decrement() + DBC_MEM_FUN( (public) (void) DBC_COPYABLE(Counter) (decrement)(), { + DBC_ASSERT(self.value() >= 1, "positive value"); + }, { + DBC_ASSERT(self.now.value() == (self.old.value() - 1), + "decremented value"); + }, { + --value_; + notify(); + }) + +private: + int value_; + + DBC_INVARIANT(Counter, {}) +}; + +#endif // COUNTER_HPP_ + diff --git a/example/DBC_by_Example/Counter/DecrementButton.hpp b/example/DBC_by_Example/Counter/DecrementButton.hpp new file mode 100755 index 00000000..f4867072 --- /dev/null +++ b/example/DBC_by_Example/Counter/DecrementButton.hpp @@ -0,0 +1,104 @@ +/* $Id$ */ + +#ifndef DECREMENT_BUTTON_HPP_ +#define DECREMENT_BUTTON_HPP_ + +#include "PushButton.hpp" +#include "Counter.hpp" +#include "../Observer/Observer.hpp" +#include +#include + +/** Button to crement an associated counter. */ +class DecrementButton: public PushButton, protected Observer + DBC_TRAILING_OBJECT(DecrementButton) { +public: + // Creation // + + /** Create button associated to specified counter. */ + DecrementButton(Counter& counter): counterRef_(counter), + counter_(counter) + DBC_CONSTRUCTOR( (public) (DecrementButton)( (Counter&)(counter) ), { + }, { + DBC_ASSERT( + self.now.enabled() == (self.now.counter().value() > 0), + "enabled consistent with counter value"); + }, { + counterRef_.attach(this); + counter_ = counterRef_; + }) + + /** Destroy button. */ + virtual ~DecrementButton() + DBC_DESTRUCTOR( (public) (virtual) (DecrementButton)(), {}) + + // Commands // + + void onBnClicked() + DBC_MEM_FUN( (public) (void) DBC_COPYABLE(DecrementButton) + (onBnClicked)(), { + DBC_ASSERT(self.enabled(), "enabled"); + }, { + DBC_ASSERT(self.now.counter().value() + == (self.old.counter().value() - 1), "counter decremented"); + }, { + // Check this assertion within body code. + DBC_ASSERT(enabled() == (counterRef().value() > 0), + "enabled consistent with counter value"); + counterRef_.decrement(); + counter_ = counterRef_; + }) + +protected: + /** Return counter (by refernce). */ + const Counter& counterRef() const + DBC_MEM_FUN( (protected) (const Counter&) (DecrementButton) + (counterRef)() (const), { + }, { + }, { + return counterRef_; + }) + + /** Return counter (by value). */ + const Counter& counter() const + DBC_MEM_FUN( (protected) (const Counter&) (DecrementButton) + (counter)() (const), { + }, { + }, { + return counter_; + }) + +private: + bool upToDateWithSubject() const + DBC_MEM_FUN( (private) (bool) (DecrementButton)DBC_BASE(Observer) + (upToDateWithSubject)() (const), { + }, { + }, { + return true; + }) + + void update() + DBC_MEM_FUN( (public) (void) (DecrementButton)DBC_BASE(Observer) + (update)(), { + }, { + DBC_ASSERT(self.now.enabled() == (self.now.counterRef().value() > 0), + "enabled consistent with counter value"); + }, { + if (0 == counterRef_.value()) { disable(); } + else { enable(); } + }) + + Counter& counterRef_; + // This is needed just so the counter value is actually copied + // by post<> in self.old to correctly evaluate the post-conditions of + // onBnClicked. This is a complication when dealing with ref/ptr in the + // object state as the default copy will be shallow (I also could have + // impl my own copy constructor and operator= to do a deep copy, but I + // still would have to keep both counterRef_ and the actual counter). + Counter counter_; + + DBC_INVARIANT(DecrementButton, {}) +}; + +#endif// DECREMENT_BUTTON_HPP_ + diff --git a/example/DBC_by_Example/Counter/PushButton.hpp b/example/DBC_by_Example/Counter/PushButton.hpp new file mode 100755 index 00000000..22b4a75e --- /dev/null +++ b/example/DBC_by_Example/Counter/PushButton.hpp @@ -0,0 +1,29 @@ +/* $Id$ */ + +#ifndef PUSH_BUTTON_HPP_ +#define PUSH_BUTTON_HPP_ + +/** A button. */ +class PushButton { +public: + // Creation // + + /** Create button. */ + PushButton(): enabled_(true) {} + + /** Invoked by external event handler when this button has been clicked. */ + virtual void onBnClicked() = 0; + + /** If button enabled. */ + bool enabled() const { return enabled_; } + /** Enable this button. */ + void enable() { enabled_ = true; } + /** Disable this button. */ + void disable() { enabled_ = false; } + +private: + bool enabled_; +}; + +#endif // PUSH_BUTTON_HPP_ + diff --git a/example/DBC_by_Example/Counter/ViewOfCounter.hpp b/example/DBC_by_Example/Counter/ViewOfCounter.hpp new file mode 100755 index 00000000..e2c5bf7d --- /dev/null +++ b/example/DBC_by_Example/Counter/ViewOfCounter.hpp @@ -0,0 +1,43 @@ +/* $Id$ */ + +#ifndef VIEW_OF_COUNTER_HPP_ +#define VIEW_OF_COUNTER_HPP_ + +#include "../Observer/Observer.hpp" +#include "Counter.hpp" + +/** Show current value of associated counter. */ +class ViewOfCounter: private Observer DBC_TRAILING_OBJECT(ViewOfCounter) { +public: + // Creation // + + /** Create viewer associated to specified counter. */ + ViewOfCounter(Counter& counter): counter_(counter) { + counter.attach(this); + std::cout << "Counter started at " << counter_.value() << std::endl; + } + +private: + bool upToDateWithSubject() const + DBC_MEM_FUN( (private) (bool) (ViewOfCounter)DBC_BASE(Observer) + (upToDateWithSubject)() (const), { + }, { + }, { + return true; + }) + + void update() + DBC_MEM_FUN( (public) (void) (ViewOfCounter)DBC_BASE(Observer) + (update)(), { + }, { + }, { + std::cout << "Counter changed to " << counter_.value() << std::endl; + }) + + Counter& counter_; + + DBC_INVARIANT(ViewOfCounter, {}) +}; + +#endif // VIEW_OF_COUNTER_HPP_ + diff --git a/example/DBC_by_Example/Counter/main.cpp b/example/DBC_by_Example/Counter/main.cpp new file mode 100755 index 00000000..cf336218 --- /dev/null +++ b/example/DBC_by_Example/Counter/main.cpp @@ -0,0 +1,37 @@ +/* $Id$ */ + +#include "Counter.hpp" +#include "DecrementButton.hpp" +#include "ViewOfCounter.hpp" +#include + +int main() { + Counter cnt(3); + ViewOfCounter view(cnt); + DecrementButton dec(cnt); + + char ch = '\0'; + while (ch != 'q') { + std::cout << std::endl; + std::cout << "-- Counter value printer above as " + << "\"Counter started at \" or " + << "\"Counter changed to \" --" << std::endl; + if (dec.enabled()) { + std::cout << "(-) Decrement" << std::endl; + } + std::cout << "(q) Quit" << std::endl; + std::cout << "Select: "; + + std::cin >> ch; + if ('q' == ch) { + return 0; + } else if ('-' == ch && dec.enabled()) { + dec.onBnClicked(); + } else { + std::cout << "Invalid selection '" << ch << "'" << std::endl; + } + } + + return 0; +} + diff --git a/example/DBC_by_Example/Courier/couriers.cpp b/example/DBC_by_Example/Courier/couriers.cpp new file mode 100755 index 00000000..5fd29864 --- /dev/null +++ b/example/DBC_by_Example/Courier/couriers.cpp @@ -0,0 +1,19 @@ +/* $Id$ */ + +#include "couriers.hpp" + +double Courier::DBC_BODY(insuranceCoverDollar)() const + { return insuranceCoverDollar_; } + +void Courier::DBC_BODY(deliver)(Package& package, + const std::string& destination) { + package.location = destination; + package.deliveredHour = package.acceptedHour + 2.5; +} + +void DifferentCourier::DBC_BODY(deliver)( + Package& package, const std::string& destination) { + package.location = destination; + package.deliveredHour = package.acceptedHour + 0.5; +} + diff --git a/example/DBC_by_Example/Courier/couriers.hpp b/example/DBC_by_Example/Courier/couriers.hpp new file mode 100755 index 00000000..47e3d9a5 --- /dev/null +++ b/example/DBC_by_Example/Courier/couriers.hpp @@ -0,0 +1,138 @@ +/* $Id$ */ + +#ifndef COURIERS_HPP_ +#define COURIERS_HPP_ + +#include +#include + +/** Basic package information. */ +class Package { +public: + /** Package weight in kilograms. */ + double weightKg; + /** Package current location. */ + std::string location; + /** Hour at which package was accepted for delivery (for "zero hours"). */ + double acceptedHour; + /** Hour at which package was delivered (from "zero hours"). */ + double deliveredHour; + + /** Construct a package with specified information. */ + Package(const double& theWeightKg, const std::string& theLocation = "", + const double& theAcceptedHour = 0.0, + const double& theDeliveredHour = 0.0): + weightKg(theWeightKg), location(theLocation), + acceptedHour(theAcceptedHour), deliveredHour(theDeliveredHour) {} +}; + +/** A basic courier for package delivery. */ +class Courier DBC_INHERIT_OBJECT(Courier) { +public: + // Creation // + + /** Create courier with specified insurance coverage. */ + Courier(const double& insuranceCoverDollar = 10.0e+6): + insuranceCoverDollar_(insuranceCoverDollar) + DBC_CONSTRUCTOR( (public) (Courier) + ( (const double&)(insuranceCoverDollar) ), { + DBC_ASSERT(insuranceCoverDollar > 0.0, "positive insurance $"); + }, { + }, {}) + + /** Destroy this object. */ + virtual ~Courier() + DBC_DESTRUCTOR( (public) (Courier)(), {}) + + // Queries // + + /** Return insurance cover. */ + double insuranceCoverDollar() const + DBC_MEM_FUN( (public) (double) (Courier) + (insuranceCoverDollar)() (const), { + }, { + }, ;) + + // Commands // + + /** Deliver given package at specified destination. */ + virtual void deliver(Package& package, const std::string& destination) + DBC_MEM_FUN( (public) (virtual) (void) (Courier) + (deliver)( (Package&)(package) + (const std::string&)(destination) ), { + DBC_ASSERT_STREAM(package.weightKg <= 5, "max weight", + err << "weight " << package.weightKg << " kg exceeds 5 kg"); + }, { + DBC_ASSERT_STREAM( + (package.now.deliveredHour - package.now.acceptedHour) <= 3, + "max delivery time", + err << "delivered at " << package.now.deliveredHour + << " hours in more than 3 working hours of accepted hour " + << package.now.acceptedHour); + DBC_ASSERT_STREAM(package.now.location == destination.now, + "at destination", + err << "location '" << package.now.location + << "' is not destination '" << destination.now << "'"); + }, ;) + +private: + double insuranceCoverDollar_; + + DBC_INVARIANT(Courier, { + DBC_ASSERT_STREAM(self.insuranceCoverDollar() >= 10.0e+6, + "min insurance", + err << "insured $" << self.insuranceCoverDollar() + << " less than $10.0e+6"); + }) +}; + +/** A different courier for package delivery. */ +class DifferentCourier: public Courier DBC_TRAILING_OBJECT(DifferentCourier) { +public: + // Creation // + + /** Create different courier with specified insurance coverage. */ + DifferentCourier(const double& insuranceCoverDollar = 20.0e+6): + Courier(insuranceCoverDollar) + DBC_CONSTRUCTOR( (public) (DifferentCourier) + ( (const double&)(insuranceCoverDollar) ), { + DBC_ASSERT(insuranceCoverDollar > 0.0, "positive insurance $"); + }, { + }, {}) + + /** Destroy this object. */ + virtual ~DifferentCourier() + DBC_DESTRUCTOR( (public) (DifferentCourier)(), {}) + + // Commands // + + virtual void deliver(Package& package, const std::string& destination) + DBC_MEM_FUN( (public) (virtual) (void) (DifferentCourier)DBC_BASE(Courier) + (deliver)( (Package&)(package) + (const std::string&)(destination) ), { + DBC_ASSERT_STREAM(package.weightKg <= 8, + "min weight", + err << "weight " << package.weightKg + << " kg does exceeds 8 kg"); + }, { + DBC_ASSERT_STREAM( + (package.now.deliveredHour - package.now.acceptedHour) <= 2, + "min delivery time", + err << "delivered at " << package.now.deliveredHour + << " hours in more than 2 working hours of accepted hour " + << package.now.acceptedHour); + // Postcondition "at destination" inherited unchanged from base + // contract -- no need to re-write it here. + }, ;) + +private: + DBC_INVARIANT(DifferentCourier, { + DBC_ASSERT_STREAM(self.insuranceCoverDollar() >= 20.0e+6, + "min insurance", + err << "insured $" << self.insuranceCoverDollar() + << " less than $20.0e+6"); + }) +}; + +#endif // COURIERS_HPP_ + diff --git a/example/DBC_by_Example/Courier/main.cpp b/example/DBC_by_Example/Courier/main.cpp new file mode 100755 index 00000000..7afdd800 --- /dev/null +++ b/example/DBC_by_Example/Courier/main.cpp @@ -0,0 +1,30 @@ +/* $Id$ */ + +#include "couriers.hpp" +#include + +int main() { + { + std::cout << std::endl << "Init Courier..." << std::endl; + Courier c; + { + std::cout << std::endl << "Init DifferentCourier..." << std::endl; + DifferentCourier dc; + Package cups(3.6, "store"); + Package desk(7.2, "store"); + + std::cout << std::endl << "Delivering some cups home " + << "(no base pre/post/invariant checks)..." << std::endl; + c.deliver(cups, "home"); + + std::cout << std::endl << "Delivering a desk to the office " + << "(base pre/post/invariant checks)..." << std::endl; + dc.deliver(desk, "office"); + + std::cout << std::endl << "Del DifferentCourier..." << std::endl; + } + std::cout << std::endl << "Del Courier..." << std::endl; + } + return 0; +} + diff --git a/example/DBC_by_Example/CustomerManager/CustomerManager.cpp b/example/DBC_by_Example/CustomerManager/CustomerManager.cpp new file mode 100755 index 00000000..214b8ba3 --- /dev/null +++ b/example/DBC_by_Example/CustomerManager/CustomerManager.cpp @@ -0,0 +1,20 @@ +/* $Id$ */ + +#include "CustomerManager.hpp" + +int CustomerManager::DBC_BODY(count)() const { return customers_.size(); } + +bool CustomerManager::DBC_BODY(idActive)(const Customer::Id& id) const + { return customers_.end() != customers_.find(id); } + +void CustomerManager::DBC_BODY(add)(const BasicCustomerDetails& customer) + { customers_[customer.id] = Customer(customer); } + +std::string CustomerManager::DBC_BODY(nameFor)(const Customer::Id& id) const + { return customers_.find(id)->second.name; } + +void CustomerManager::DBC_BODY(setName)(const Customer::Id& id, + const std::string& name) { + customers_[id].name = name; +} + diff --git a/example/DBC_by_Example/CustomerManager/CustomerManager.hpp b/example/DBC_by_Example/CustomerManager/CustomerManager.hpp new file mode 100755 index 00000000..ca74fc57 --- /dev/null +++ b/example/DBC_by_Example/CustomerManager/CustomerManager.hpp @@ -0,0 +1,117 @@ +/* $Id$ */ + +#ifndef CUSTOMER_MANAGER_HPP_ +#define CUSTOMER_MANAGER_HPP_ + +#include +#include +#include +#include + +/** Bsic customer information. */ +class BasicCustomerDetails { +public: + /** Identifier type. */ + typedef std::string Id; + + /** Customer identifier. */ + Id id; + /** Customer name. */ + std::string name; + /** Customer address. */ + std::string address; + /** Customer birthday. */ + std::string dateOfBirth; + + /** Construct basic customer information with specified initial values. */ + BasicCustomerDetails(const Id& theId = "", const std::string& theName = "", + const std::string& theAddress = "", + const std::string& theDateOfBirth = ""): + id(theId), name(theName), address(theAddress), + dateOfBirth(theDateOfBirth) {} +}; + +/** Basic customer agent. */ +class Agent {}; + +/** Basic customer. */ +class Customer: public BasicCustomerDetails { +public: + /** Customer agent. */ + Agent managingAgent; + /** Customer last contacted. */ + std::string lastContact; + + /** Construct customer with specified initial informations. */ + Customer(const BasicCustomerDetails& basic = BasicCustomerDetails()): + BasicCustomerDetails(basic), managingAgent(), lastContact() {} +}; + +/** Manage customers. */ +class CustomerManager DBC_INHERIT_OBJECT(CustomerManager) { +public: + CustomerManager(): customers_() + DBC_CONSTRUCTOR( (public) (CustomerManager)(), { + }, { + }, {}) + + virtual ~CustomerManager() + DBC_DESTRUCTOR( (public) (virtual) (CustomerManager)(), {}) + + // Basic Queries // + + /** Return number of customers. */ + int count() const + DBC_MEM_FUN( (public) (int) (CustomerManager) (count)() (const), { + }, { + }, ;) + + /** Is there a customer with specified ID. */ + bool idActive(const Customer::Id& id) const + DBC_MEM_FUN( (public) (bool) (CustomerManager) + (idActive)( (const Customer::Id&)(id) ) (const), { + }, { + }, ;) + + // Derived Queries // + + /** Return name of the customer with specified ID. */ + std::string nameFor(const Customer::Id& id) const + DBC_MEM_FUN( (public) (std::string) (CustomerManager) + (nameFor)( (const Customer::Id&)(id) ) (const), { + DBC_ASSERT(self.idActive(id), "id active"); + }, { + }, ;) + + // Commands // + + /** Add the specified customer. */ + void add(const BasicCustomerDetails& customer) + DBC_MEM_FUN( (public) (void) DBC_COPYABLE(CustomerManager) + (add)( (const BasicCustomerDetails&)(customer) ), { + DBC_ASSERT(!self.idActive(customer.id), "id not already active"); + }, { + DBC_ASSERT(self.now.count() == (self.old.count() + 1), + "count increased"); + DBC_ASSERT(self.now.idActive(customer.now.id), "id now active"); + }, ;) + + /** Set name of the customer with speficied ID. */ + void setName(const Customer::Id& id, const std::string& name) + DBC_MEM_FUN( (public) (void) (CustomerManager) (setName) + ( (const Customer::Id&)(id) (const std::string&)(name) ), { + DBC_ASSERT(self.idActive(id), "id active"); + }, { + DBC_ASSERT(self.now.nameFor(id.now) == name.now, "name set"); + }, ;) + +private: + std::map customers_; + + DBC_INVARIANT(CustomerManager, { + DBC_ASSERT(self.count() >= 0, "count never negative"); + }) +}; + +#endif // CUSTOMER_MANAGER_HPP_ + diff --git a/example/DBC_by_Example/CustomerManager/main.cpp b/example/DBC_by_Example/CustomerManager/main.cpp new file mode 100755 index 00000000..71a5e80d --- /dev/null +++ b/example/DBC_by_Example/CustomerManager/main.cpp @@ -0,0 +1,29 @@ +/* $Id$ */ + +#include "CustomerManager.hpp" + +int main() { + std::cout << "init()..." << std::endl; + CustomerManager mgr; + + BasicCustomerDetails c("sci", "Leonardo Da Vinci", + "Florence, Italy", "Year 1452"); + std::cout << std::endl << "add()..." << std::endl; + mgr.add(c); + + std::cout << std::endl << "setName()..." << std::endl; + mgr.setName("sci", "Galileo Galileo"); + + std::cout << std::endl << "nameFor()..." << std::endl; + std::cout << mgr.nameFor("sci"); + + std::cout << std::endl << "count()..." << std::endl; + std::cout << mgr.count() << std::endl; + + std::cout << std::endl << "idActive()..." << std::endl; + std::cout << mgr.idActive("sci") << std::endl; + + std::cout << std::endl << "del()..." << std::endl; + return 0; +} + diff --git a/example/DBC_by_Example/Dictionary/Dictionary.hpp b/example/DBC_by_Example/Dictionary/Dictionary.hpp new file mode 100755 index 00000000..45119de3 --- /dev/null +++ b/example/DBC_by_Example/Dictionary/Dictionary.hpp @@ -0,0 +1,98 @@ +/* $Id$ */ + +#ifndef DICTIONARY_HPP_ +#define DICTIONARY_HPP_ + +#include +#include + +/** Simple dictionary. */ +template +class Dictionary DBC_INHERIT_OBJECT(DBC_MPARAM(2, (Dictionary))) { +public: + // Creation // + + /** Create an empty dictionary. */ + Dictionary() + DBC_CONSTRUCTOR( (public) (template)(Dictionary)(), { + }, { + DBC_ASSERT(0 == self.now.count(), "is empty"); + }, {}) + + virtual ~Dictionary() + DBC_DESTRUCTOR( (public) (virtual) (template)(Dictionary)(), {}) + + // Basic Queries // + + /** Number of keys in dictionary. */ + int count() const + DBC_MEM_FUN( (public) (int) (template)(Dictionary) (count)() (const), { + }, { + DBC_ASSERT(result >= 0, "returning non-negative"); + }, { + return items_.size(); + }) + + /** If the dictionary contain specified key. */ + bool has(const Key& key) const + DBC_MEM_FUN( (public) (bool) (template)(Dictionary) + (has)( (const Key&)(key) ) (const), { + }, { + if (0 == self.now.count()) DBC_ASSERT(!result, "if empty, has no key"); + }, { + return items_.end() != items_.find(key); + }) + + /** Value associated with specified key. */ + const Value& valueFor(const Key& key) const + DBC_MEM_FUN( (public) (const Value&) (template)(Dictionary) + (valueFor)( (const Key&)(key) ) (const), { + DBC_ASSERT(self.has(key), "key in dictionary"); + }, { + }, { + return items_.find(key)->second; + }) + + // Commands // + + /** Put specified key into dictionary with specified value. */ + void put(const Key& key, const Value& value) + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(Dictionary) + (put)( (const Key&)(key) (const Value&)(value) ), { + DBC_ASSERT(!self.has(key), "key not in dictionary"); + }, { + DBC_ASSERT_STREAM(self.now.count() == (self.old.count() + 1), + "one more value", + err << "count " << self.now.count() << " not increased from " + << self.old.count()); + DBC_ASSERT(self.now.has(key.now), "key in dictionary"); + DBC_ASSERT(self.now.valueFor(key.now) == value.now, + "set value for key"); + }, { + items_[key] = value; + }) + + /** Remove specified key and its value from dictionary. */ + void remove(const Key& key) + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(Dictionary) + (remove)( (const Key&)(key) ), { + DBC_ASSERT(self.has(key), "key in dictionary"); + }, { + DBC_ASSERT(self.now.count() == (self.old.count() - 1), + "count decreased"); + DBC_ASSERT(!self.now.has(key.now), "key not in dictionary"); + }, { + items_.erase(key); + }) + +private: + std::map items_; + + DBC_INVARIANT(Dictionary, { + DBC_ASSERT_STREAM(self.count() >= 0, "never negative", + err << "count " << self.count() << " is negative"); + }) +}; + +#endif // DICTIONARY_HPP_ + diff --git a/example/DBC_by_Example/Dictionary/main.cpp b/example/DBC_by_Example/Dictionary/main.cpp new file mode 100755 index 00000000..552a0e0f --- /dev/null +++ b/example/DBC_by_Example/Dictionary/main.cpp @@ -0,0 +1,29 @@ +/* $Id$ */ + +#include "Dictionary.hpp" +#include +#include + +int main() { + std::cout << std::endl << "init()..." << std::endl; + Dictionary ages; + + std::cout << std::endl << "has()..." << std::endl; + std::cout << ages.has("Galileo Galilei") << std::endl; + + std::cout << std::endl << "put()..." << std::endl; + ages.put("Galileo Galilei", 400); + + std::cout << std::endl << "valueFor()..." << std::endl; + std::cout << ages.valueFor("Galileo Galilei") << std::endl; + + std::cout << std::endl << "count()..." << std::endl; + std::cout << ages.count() << std::endl; + + std::cout << std::endl << "remove()..." << std::endl; + ages.remove("Galileo Galilei"); + + std::cout << std::endl << "del()..." << std::endl; + return 0; +} + diff --git a/example/DBC_by_Example/NameList/main.cpp b/example/DBC_by_Example/NameList/main.cpp new file mode 100755 index 00000000..c7dba81e --- /dev/null +++ b/example/DBC_by_Example/NameList/main.cpp @@ -0,0 +1,26 @@ +/* $Id$ */ + +#include "names.hpp" +#include + +int main() { + std::cout << std::endl << "init()..." << std::endl; + std::string n; + NameList nl; + RelaxedNameList rl; + + n = "Leonardo Da Vinci"; + std::cout << std::endl << "put()..." << std::endl; + nl.put(n); + + n = "Galileo Galilei"; + std::cout << std::endl << "put()..." << std::endl; + rl.put(n); + std::cout << std::endl + << "put() again (allowed by RelaxedNameList only)..." << std::endl; + rl.put(n); + + std::cout << std::endl << "del()..." << std::endl; + return 0; +} + diff --git a/example/DBC_by_Example/NameList/names.cpp b/example/DBC_by_Example/NameList/names.cpp new file mode 100755 index 00000000..f8047e51 --- /dev/null +++ b/example/DBC_by_Example/NameList/names.cpp @@ -0,0 +1,23 @@ +/* $Id$ */ + +#include "names.hpp" +#include + +bool NameList::DBC_BODY(has)(const std::string& name) const + { return names_.end() != std::find(names_.begin(), names_.end(), name); } + +unsigned int NameList::DBC_BODY(count)() const { return names_.size(); } + +void NameList::DBC_BODY(put)(const std::string& name) + { names_.push_back(name); } + +void RelaxedNameList::DBC_BODY(put)(const std::string& name) { + if (!has(name)) { + /** @note When invoking base class implementation of derived member + * function, you must use the DBC_BODY() qualifier to avoid infinite + * contract checking recursion. In this case, "NameList::put(name);" + * would have caused an infinite recursion. */ + NameList::DBC_BODY(put)(name); + } +} + diff --git a/example/DBC_by_Example/NameList/names.hpp b/example/DBC_by_Example/NameList/names.hpp new file mode 100755 index 00000000..a7115e60 --- /dev/null +++ b/example/DBC_by_Example/NameList/names.hpp @@ -0,0 +1,100 @@ +/* $Id$ */ + +#ifndef NAMES_HPP_ +#define NAMES_HPP_ + +#include +#include +#include + +/** List of names. */ +class NameList DBC_INHERIT_OBJECT(NameList) { +public: + // Creation // + + NameList() + DBC_CONSTRUCTOR( (public) (NameList)(), { + }, { + }, {}) + + virtual ~NameList() + DBC_DESTRUCTOR( (public) (virtual) (NameList)(), {}) + + // Queries // + + /** If specified name is in list. */ + bool has(const std::string& name) const + DBC_MEM_FUN( (public) (bool) (NameList) + (has)( (const std::string&)(name) ) (const), { + }, { + }, ;) + + /** Return number of names in list. */ + unsigned int count() const + DBC_MEM_FUN( (public) (unsigned int) (NameList) (count)() (const), { + }, { + }, ;) + + // Commands // + + /** Add specified name to list. */ + virtual void put(const std::string& name) + DBC_MEM_FUN( (public) (virtual) (void) DBC_COPYABLE(NameList) + (put)( DBC_COPYABLE(const std::string&)(name) ), { + DBC_ASSERT_STREAM(!self.has(name), "not in list", + err << "name '" << name << "' already in list"); + }, { + // required() is provided to facilitate writing post-condition guards + // for more flexible contracts of virtual functions. + if (required()) // Or "if (!self.old.has(name.old))". + DBC_ASSERT_STREAM(self.now.has(name.now), + "if require passed, in list", + err << "name '" << name.now << "' not in list"); + // Because name's type is const, name.now could be used here instead of + // name.old (saving to declare name's type as DBC_COPYABLE() and to + // have to copy its value). However, technically "seld.old.has( + // name.old)" is the expression equivalent to required(). + if (!self.old.has(name.old)) + DBC_ASSERT(self.now.count() == (self.old.count() + 1), + "if was not in list, count increased"); + }, ;) + +private: + std::list names_; + + DBC_INVARIANT(NameList, {}) +}; + +/** List of names that allows for duplicates. */ +class RelaxedNameList: public NameList DBC_TRAILING_OBJECT(RelaxedNameList) { +public: + // Creation // + + RelaxedNameList() + DBC_CONSTRUCTOR( (public) (RelaxedNameList)(), { + }, { + }, {}) + + virtual ~RelaxedNameList() + DBC_DESTRUCTOR( (public) (virtual) (RelaxedNameList)(), {}) + + // Commands // + + void put(const std::string& name) + DBC_MEM_FUN( (public) (void) + DBC_COPYABLE(RelaxedNameList)DBC_BASE(NameList) + (put)( DBC_COPYABLE(const std::string&)(name) ), { + DBC_ASSERT_STREAM(self.has(name), "in list", + err << "name '" << name << "' not in list"); + }, { + if (self.old.has(name.old)) + DBC_ASSERT(self.now.count() == self.old.count(), + "if in list, count unchanged"); + }, ;) + +private: + DBC_INVARIANT(RelaxedNameList, {}) +}; + +#endif // NAMES_HPP_ + diff --git a/example/DBC_by_Example/Observer/Observer.hpp b/example/DBC_by_Example/Observer/Observer.hpp new file mode 100755 index 00000000..a0a8b3ba --- /dev/null +++ b/example/DBC_by_Example/Observer/Observer.hpp @@ -0,0 +1,46 @@ +/* $Id$ */ + +#ifndef OBSERVER_HPP_ +#define OBSERVER_HPP_ + +#include + +/** Observer for "observer framework". */ +class Observer DBC_INHERIT_OBJECT(Observer) { + friend class Subject; // Subject invokes update(). +public: + // Creation // + + /** Constructor observer. */ + Observer() + DBC_CONSTRUCTOR( (public) (Observer)(), { + }, { + }, {}) + + /** Destroy observer. */ + virtual ~Observer() + DBC_DESTRUCTOR( (public) (virtual) (Observer)(), {}) + +protected: + // Commands // + + /** If up to date with its subject. */ + virtual bool upToDateWithSubject() const + DBC_MEM_FUN( (protected) (virtual) (bool) (Observer) + (upToDateWithSubject)() (const), { + }, { + }, = 0;) + + /** Bring this observer up to date with its subject. */ + virtual void update() + DBC_MEM_FUN( (protected) (virtual) (void) (Observer) (update)(), { + }, { + DBC_ASSERT(self.now.upToDateWithSubject(), "up-to-date with subject"); + }, = 0;) + +private: + DBC_INVARIANT(Observer, {}) +}; + +#endif // OBSERVER_HPP_ + diff --git a/example/DBC_by_Example/Observer/Subject.hpp b/example/DBC_by_Example/Observer/Subject.hpp new file mode 100755 index 00000000..7a615b9b --- /dev/null +++ b/example/DBC_by_Example/Observer/Subject.hpp @@ -0,0 +1,117 @@ +/* $Id$ */ + +#ifndef SUBJECT_HPP_ +#define SUBJECT_HPP_ + +#include "Observer.hpp" +#include +#include +#include + +/** Subject for "observer framework". */ +class Subject DBC_INHERIT_OBJECT(Subject) { +public: + // Creation // + + /** Construct subject. */ + Subject() + DBC_CONSTRUCTOR( (public) (Subject)(), { + }, { + }, {}) + + virtual ~Subject() + DBC_DESTRUCTOR( (public) (virtual) (Subject)(), {}) + + // Queries // + + /** If specified observer is attached. */ + bool attached(const Observer* o) const + DBC_MEM_FUN( (public) (bool) (Subject) + (attached)( (const Observer*)(o) ) (const), { + }, { + const std::list& os = self.now.observers(); + DBC_ASSERT( + result == (os.end() != std::find(os.begin(), os.end(), o.now)), + "result consistent with observers"); + }, { + return observers_.end() != std::find(observers_.begin(), + observers_.end(), o); + }) + + // Commands // + + /** Remember specified object as on of this subject's observers. */ + void attach(Observer* o) + DBC_MEM_FUN( (public) (void) DBC_COPYABLE(Subject) + (attach)( DBC_COPYABLE(Observer*)(o) ), { + DBC_ASSERT(o, "observer exists"); + DBC_ASSERT_STREAM(!self.attached(o), "not alreayd attached", + err << "observer " << o << " alreayd attached"); + }, { + DBC_ASSERT_STREAM(self.now.attached(o.now), "attached", + err << "observer " << o.now << " not attached"); + + // Frame rule (could be written more compactly...). + std::list oldOs = self.old.observers(); + std::list os = self.now.observers(); + std::remove(os.begin(), os.end(), o.old); + std::list::iterator oIt = os.begin(); + std::list::iterator oldOIt = oldOs.begin(); + while (os.end() != oIt && oldOs.end() != oldOIt) { + DBC_ASSERT_STREAM(*oIt == *oldOIt, "all other observers unchanged", + err << "observer " << *oIt << " changed from " + << *oldOIt); + ++oIt; + ++oldOIt; + } + }, { + observers_.push_back(o); + }) + +protected: + /** Update all attached observers. */ + void notify() + DBC_MEM_FUN( (protected) (void) (Subject) (notify)(), { + }, { + const std::list& os = self.now.observers(); + for (std::list::const_iterator i = os.begin(); + os.end() != i; ++i) { + const Observer* o = *i; + DBC_ASSERT(o, "observer exists"); + DBC_ASSERT_STREAM(o->upToDateWithSubject(), "up-to-date", + err << "observer " << o << " not up-to-date"); + } + }, { + for (std::list::iterator i = observers_.begin(); + observers_.end() != i; ++i) { + Observer* o = *i; + if (o) { o->update(); } + } + }) + + /** All observers attached to this subject. */ + // This is protected as it is intended to support specification only and + // should NOT be used as part of the public API of this class. However, + // derived classes can still use this query to wirte their contracts. See + // related discussion in book "Design By Contract, by Example" Section 9.9. + const std::list observers() const + DBC_MEM_FUN( (protected) (const std::list) (Subject) + (observers)() (const), { + }, { + }, { + std::list os; + for (std::list::const_iterator i = observers_.begin(); + observers_.end() != i; ++i) { + os.push_back(*i); + } + return os; + }) + +private: + DBC_INVARIANT(Subject, {}) + + std::list observers_; +}; + +#endif // SUBJECT_HPP_ + diff --git a/example/DBC_by_Example/Observer/main.cpp b/example/DBC_by_Example/Observer/main.cpp new file mode 100755 index 00000000..92a40cc3 --- /dev/null +++ b/example/DBC_by_Example/Observer/main.cpp @@ -0,0 +1,77 @@ +/* $Id$ */ + +#include "Subject.hpp" +#include +#include + +/** Implementation of abstract subject. */ +class ConcreteSubject: public Subject { +public: + /** State being observed. */ + typedef int State; + + ConcreteSubject(): state_() {} + + /** Set state being observed. */ + void setState(const State& state) { + std::cout << "Changing state to " << state << std::endl; + state_ = state; + notify(); + } + + /** Get state being observed. */ + State getState() const { return state_; } + +private: + State state_; +}; + +/** Implementation of abstract observer. */ +class ConcreteObserver: public Observer { +public: + /** Creates concrete observer. */ + ConcreteObserver(const ConcreteSubject& subject): subject_(subject), + observedState_() {} + +private: + bool upToDateWithSubject() const + DBC_MEM_FUN( (private) (bool) (ConcreteObserver)DBC_BASE(Observer) + (upToDateWithSubject)() (const), { + }, { + }, { + return true; + }) + + void update() + DBC_MEM_FUN( (private) (void) (ConcreteObserver)DBC_BASE(Observer) + (update)(), { + }, { + }, { + observedState_ = subject_.getState(); + std::cout << "Observed state " << observedState_ << std::endl; + }) + + const ConcreteSubject& subject_; + ConcreteSubject::State observedState_; +}; + +int main() { + ConcreteSubject s; + ConcreteObserver o(s); + + std::cout << std::endl << "Attaching observer " << &o << "..." + << std::endl; + s.attach(&o); + + ConcreteSubject::State st = -10; + std::cout << std::endl << "Setting state to " << st << "..." << std::endl; + s.setState(st); + + st = +10; + std::cout << std::endl << "Re-setting state to " << st << "..." + << std::endl; + s.setState(st); + + return 0; +} + diff --git a/example/DBC_by_Example/README.txt b/example/DBC_by_Example/README.txt new file mode 100755 index 00000000..95d1b215 --- /dev/null +++ b/example/DBC_by_Example/README.txt @@ -0,0 +1 @@ +Implemented examples from book "Design by Contract, by Example", R. Mitchell, and J. McKim. \ No newline at end of file diff --git a/example/DBC_by_Example/SimpleQueue/SimpleQueue.hpp b/example/DBC_by_Example/SimpleQueue/SimpleQueue.hpp new file mode 100755 index 00000000..656e74ff --- /dev/null +++ b/example/DBC_by_Example/SimpleQueue/SimpleQueue.hpp @@ -0,0 +1,157 @@ +/* $Id$ */ + +#ifndef SIMPLE_QUEUE_HPP_ +#define SIMPLE_QUEUE_HPP_ + +#include +#include + +/** Simple queue. */ +template +class SimpleQueue DBC_INHERIT_OBJECT(SimpleQueue) { +public: + // Creation // + + /** Create empty queue. */ + SimpleQueue(const int& theCapacity): items_() + DBC_CONSTRUCTOR( (public) (template)(SimpleQueue) + ( (const int&)(theCapacity) ), { + DBC_ASSERT(theCapacity >= 1, "capacity at least 1"); + }, { + DBC_ASSERT_STREAM(self.now.capacity() == theCapacity.now, + "capacity set", + err << "capacity " << self.now.capacity() << " not set to " + << theCapacity.now); + DBC_ASSERT(self.now.isEmpty(), "is empty"); + }, { + items_.reserve(theCapacity); + }) + + virtual ~SimpleQueue() + DBC_DESTRUCTOR( (public) (virtual) (template)(SimpleQueue)(), {}) + + // Basic Queries // + + /** Items in queue in their order. */ + const std::vector items() const + DBC_MEM_FUN( (public) (const std::vector) (template)(SimpleQueue) + (items)() (const), { + }, { + }, { + return items_; + }) + + /** Maximum number of items queue can hold. */ + int capacity() const + DBC_MEM_FUN( (public) (int) (template)(SimpleQueue) (capacity)() (const), { + }, { + }, { + return items_.capacity(); + }) + + // Derived Queries // + + /** Number of items in the queue. */ + int count() const + DBC_MEM_FUN( (public) (int) (template)(SimpleQueue) (count)() (const), { + }, { + DBC_ASSERT(result == int(self.now.items().size()), + "result consistent with size"); + }, { + return items_.size(); + }) + + /** Item at the head. */ + const T& head() const + DBC_MEM_FUN( (public) (const T&) (template)(SimpleQueue) (head)() (const), { + DBC_ASSERT(!self.isEmpty(), "not empty"); + }, { + DBC_ASSERT(result == self.now.items().at(0), "result is item's top"); + }, { + return items_.at(0); + }) + + /** If queue contains no items. */ + bool isEmpty() const + DBC_MEM_FUN( (public) (bool) (template)(SimpleQueue) (isEmpty)() (const), { + }, { + DBC_ASSERT(result == (0 == self.now.count()), + "result consistent with count "); + }, { + return 0 == items_.size(); + }) + + /** If queue has no room for another item. */ + bool isFull() const + DBC_MEM_FUN( (public) (bool) (template)(SimpleQueue) (isFull)() (const), { + }, { + DBC_ASSERT_STREAM(result == (int(self.now.items().size()) + == self.now.capacity()), + "result consistent with count and capacity", + err << "result " << result + << " inconsistent with items' count " + << self.now.items().size() << " and capacity " + << self.now.capacity()); + }, { + return items_.size() == items_.capacity(); + }) + + // Commands // + + /** Remove head item shifting all other items accordingly. */ + void remove() + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(SimpleQueue) + (remove)(), { + DBC_ASSERT(!self.isEmpty(), "not empty"); + }, { + DBC_ASSERT(self.now.count() == (self.old.count() - 1), + "number of items decreased"); + + const std::vector& items = self.now.items(); + const std::vector& oldItems = self.old.items(); + for (size_t i = 1; i < oldItems.size(); ++i) { + DBC_ASSERT_STREAM(items.at(i - 1) == oldItems.at(i), + "all items shifted", + err << "item not shifted from " << i << " to " << (i - 1)); + } + }, { + items_.erase(items_.begin()); + }) + + /** Add specified item to queue tail. */ + void put(const T& item) + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(SimpleQueue) + (put)( (const T&)(item) ), { + DBC_ASSERT(self.count() < self.capacity(), + "count not filled capacity"); + }, { + const int& count = self.now.count(); + const int& oldCount = self.old.count(); + DBC_ASSERT(count == (oldCount + 1), "count increased"); + + const std::vector items = self.now.items(); + DBC_ASSERT(items.at(count - 1) == item.now, "item at tail"); + + // Frame rule. + const std::vector oldItems = self.old.items(); + if (count >= 2) + for (int i = 0; i < oldCount; ++i) { + DBC_ASSERT_STREAM(items.at(i) == oldItems.at(i), + "existing items unchanged", + err << "original item at " << i + << " was changed"); + } + }, { + items_.push_back(item); + }) + +private: + std::vector items_; + + DBC_INVARIANT(SimpleQueue, { + DBC_ASSERT(self.count() >= 0, "count never negative"); + }) +}; + +#endif // SIMPLE_QUEUE_HPP_ + diff --git a/example/DBC_by_Example/SimpleQueue/main.cpp b/example/DBC_by_Example/SimpleQueue/main.cpp new file mode 100755 index 00000000..d1e32d3f --- /dev/null +++ b/example/DBC_by_Example/SimpleQueue/main.cpp @@ -0,0 +1,41 @@ +/* $Id$ */ + +#include "SimpleQueue.hpp" +#include + +int main() { + std::cout << std::endl << "init()..." << std::endl; + SimpleQueue q(10); + + std::cout << std::endl << "count()..." << std::endl; + std::cout << q.count() << std::endl; + + std::cout << std::endl << "put()..." << std::endl; + q.put('a'); + std::cout << std::endl << "put() again..." << std::endl; + q.put('b'); + + std::cout << std::endl << "items()..." << std::endl; + const std::vector& items = q.items(); + for (std::vector::const_iterator i = items.begin(); + items.end() != i; ++i) { std::cout << *i << std::endl; } + + std::cout << std::endl << "capacity()..." << std::endl; + std::cout << q.capacity() << std::endl; + + std::cout << std::endl << "head()..." << std::endl; + std::cout << q.head() << std::endl; + + std::cout << std::endl << "isEmpty()..." << std::endl; + std::cout << q.isEmpty() << std::endl; + + std::cout << std::endl << "isFull()..." << std::endl; + std::cout << q.isFull() << std::endl; + + std::cout << std::endl << "remove()..." << std::endl; + q.remove(); + + std::cout << std::endl << "del()..." << std::endl; + return 0; +} + diff --git a/example/DBC_by_Example/Stack/Stack.hpp b/example/DBC_by_Example/Stack/Stack.hpp new file mode 100755 index 00000000..c76bed5f --- /dev/null +++ b/example/DBC_by_Example/Stack/Stack.hpp @@ -0,0 +1,105 @@ +/* $Id$ */ + +#ifndef STACK_HPP_ +#define STACK_HPP_ + +#include +#include + +/** Simple stack. */ +template +class Stack DBC_INHERIT_OBJECT(Stack) { +public: + // Creation // + + /** Creates an empty stack. */ + Stack(): items_() + DBC_CONSTRUCTOR( (public) (template)(Stack)(), { + }, { + DBC_ASSERT(0 == self.now.count(), "empty"); + }, {}) + + // Basic Queries // + + /** Return number of items on stack. */ + int count() const + DBC_MEM_FUN( (public) (int) (template)(Stack) (count)() (const), { + }, { + }, { + return items_.size(); + }) + + /** Return item at specified position. + * Position count() is the top of stack, position 1 is bottom. */ + const T& itemAt(const int& index) const + DBC_MEM_FUN( (public) (const T&) (template)(Stack) + (itemAt)( (const int&)(index) ) (const), { + DBC_ASSERT(index >= 1, "index big enough"); + DBC_ASSERT(index <= self.count(), "index small enough"); + }, { + }, { + return items_.at(index - 1); + }) + + // Derived Queries // + + /** If stack contains no items. */ + bool isEmpty() const + DBC_MEM_FUN( (public) (bool) (template)(Stack) (isEmpty)() (const), { + }, { + DBC_ASSERT(result == (self.now.count() == 0), + "consistent with count"); + }, { + return items_.size() == 0; + }) + + /* Item at top of stack. */ + const T& item() const + DBC_MEM_FUN( (public) (const T&) (template)(Stack) (item)() (const), { + DBC_ASSERT(self.count() > 0, "not empty"); + }, { + const int count = self.now.count(); + const T& top = self.now.itemAt(count); + DBC_ASSERT(result == top, "consisten with item at"); + }, { + return items_.at(items_.size() - 1); + }) + + // Commands // + + /** Push specified item on top of stack. */ + void put(const T& item) + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(Stack) + (put)( (const T&)(item) ), { + }, { + const int count = self.now.count(); + const T& top = self.now.itemAt(count); + DBC_ASSERT(count == (self.old.count() + 1), "count increased"); + DBC_ASSERT(top == item.now, "item on top"); + }, { + size_t end = items_.size(); + items_.resize(end + 1); + items_.at(end) = item; + }) + + /** Pop top item from top of stack. */ + void remove() + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(Stack) (remove)(), { + DBC_ASSERT(self.count() > 0, "not empty"); + }, { + DBC_ASSERT(self.now.count() == (self.old.count() - 1), + "count decreased"); + }, { + items_.resize(items_.size() - 1); + }) + +private: + std::vector items_; + + DBC_INVARIANT(Stack, { + DBC_ASSERT(self.count() >= 0, "count never negative"); + }) +}; + +#endif // STACK_HPP_ + diff --git a/example/DBC_by_Example/Stack/main.cpp b/example/DBC_by_Example/Stack/main.cpp new file mode 100755 index 00000000..0f60842d --- /dev/null +++ b/example/DBC_by_Example/Stack/main.cpp @@ -0,0 +1,33 @@ +/* $Id$ */ + +#include "Stack.hpp" +#include +#include + +int main() { + std::cout << std::endl << "init()..." << std::endl; + Stack s; + + std::cout << std::endl << "count()..." << std::endl; + std::cout << "Stack has " << s.count() << " elements." << std::endl; + + std::cout << std::endl << "put()..." << std::endl; + s.put("Galileo Galilei"); + + std::cout << std::endl << "itemAt()..." << std::endl; + std::cout << s.itemAt(1) << std::endl; + + std::cout << std::endl << "item()..." << std::endl; + std::cout << s.item() << std::endl; + + std::cout << std::endl << "remove()..." << std::endl; + s.remove(); + + std::cout << std::endl << "isEmpty()..." << std::endl; + std::cout << s.isEmpty() << std::endl; + + std::cout << std::endl << "del()..." << std::endl; + + return 0; +} + diff --git a/example/OO_SW_Construction/Gcd/main.cpp b/example/OO_SW_Construction/Gcd/main.cpp new file mode 100755 index 00000000..3377471b --- /dev/null +++ b/example/OO_SW_Construction/Gcd/main.cpp @@ -0,0 +1,38 @@ +/* $Id$ */ + +#include +#include +#include + +int gcd(const int& a, const int& b) { + class GcdLoop: dbc::loop { + public: + int x, y; + GcdLoop(const int& a, const int& b): x(a), y(b) + { run(__FILE__, __LINE__); } + private: + void invariant() { + DBC_ASSERT(x > 0, "positive x"); + DBC_ASSERT(y > 0, "positive y"); + } + unsigned int variant() { return std::max(x, y); } + bool until() { return x == y; } + void body() { + if (x > y) { x = x - y; } + else { y = y - x; } + // The "else" above with one commented out below, intentionally + // introcuces a bug: it assigns x instead of y. This bug is catched + // by a loop variant violation -- try it! + // else { x = y - x; } + } + } gcdLoop(a, b); + return gcdLoop.x; +} + +int main() { + int a = 6, b = 12; + std::cout << "Greatest Common Divisor: GCD(" << a << ", " << b << ") = " + << gcd(a, b) << std::endl; + return 0; +} + diff --git a/example/OO_SW_Construction/README.txt b/example/OO_SW_Construction/README.txt new file mode 100755 index 00000000..5906923a --- /dev/null +++ b/example/OO_SW_Construction/README.txt @@ -0,0 +1,2 @@ +Implemented some examples from book "Object Oriented Software Contruction", +Bertrand Meyer. diff --git a/example/OO_SW_Construction/Stack3/Stack3.hpp b/example/OO_SW_Construction/Stack3/Stack3.hpp new file mode 100755 index 00000000..63557877 --- /dev/null +++ b/example/OO_SW_Construction/Stack3/Stack3.hpp @@ -0,0 +1,184 @@ +/* $Id$ */ + +#ifndef STACK3_HPP_ +#define STACK3_HPP_ + +#include "../Stack4/Stack4.hpp" +#include + +/** + * Dispenser structures with a Last-In, First-Out access policy, and a fixed + * maximum capacity. + * Tollerant version, setting an error code in case of impossible operations. + */ +template +class Stack3 DBC_INHERIT_OBJECT(Stack3) { +public: + /** Default stack item (sometime returned in case of error). */ + const T DEFAULT_ITEM; + + /** Error codes. */ + enum Error { + NONE = 0, // no error (success). + OVERFLOW, + UNDERFLOW, + NEGATIVE_SIZE, + }; + + // Initializtion // + + /** Create stack for a maximum of n elements. If n <0, set error to + * NEGATIVE_SIZE (no precondition). */ + Stack3(const int& n): DEFAULT_ITEM(), representation_(0), capacity_(), + error_(NONE) + DBC_CONSTRUCTOR( (public) (template)(Stack3)( (const int&)(n) ), { + }, { + DBC_ASSERT((n.now < 0) == (self.now.error() == NEGATIVE_SIZE), + "error code if impossible"); + DBC_ASSERT((n.now >= 0) == !self.now.error(), + "no error if possible"); + if (!self.now.error()) DBC_ASSERT(self.now.capacity() == n.now, + "capacity set if no error"); + }, { + if (n >= 0) { + capacity_ = n; + representation_ = Stack4(capacity_); + } else { + error_ = NEGATIVE_SIZE; + } + }) + + /** Destroy this stack. (Just check invariant at entry.) */ + virtual ~Stack3() + DBC_DESTRUCTOR( (public) (virtual) (template)(Stack3)(), {}) + + // Access // + + /** Maximum number of stack elements. */ + int capacity() const + DBC_MEM_FUN( (public) (int) (template)(Stack3) (capacity)() (const), { + }, { + }, { + return capacity_; + }) + + /** Number of stack elements. */ + int count() const + DBC_MEM_FUN( (public) (int) (template)(Stack3) (count)() (const), { + }, { + }, { + return representation_.count(); + }) + + /** Top element if present. Otherwise, type's default value and error set + * to UNDERFLOW (no precondition). */ + const T& item() const + DBC_MEM_FUN( (public) (const T&) (template)DBC_COPYABLE(Stack3) + (item)() (const), { + }, { + // This is a cont operation (a query), so it does not change the object + // state and there is no need to use self.old. self.now could have been + // used without making Stack3 DBC_COPYABLE() in the contract definition + // above (and saving the need of making a copy of the object). I am + // using self.old because it is used in the book where I got this + // example from -- possibly so not to have to make item() a const + // operation... + DBC_ASSERT(self.old.empty() == (self.now.error() == UNDERFLOW), + "error code if impossible"); + DBC_ASSERT(!self.old.empty() == !self.now.error(), + "no error if possible"); + }, { + if (!empty()) { + error_ = NONE; + return representation_.item(); + } else { + error_ = UNDERFLOW; + return DEFAULT_ITEM; + } + }) + + // Status Report // + + /** Error indicator, set by various operations to a non-zero (not NONE) + * value if the cannot perform their job. */ + Error error() const + DBC_MEM_FUN( (public) (Error) (template)(Stack3) (error)() (const), { + }, { + }, { return error_; + }) + + /** If stack has no item. */ + bool empty() const + DBC_MEM_FUN( (public) (bool) (template)(Stack3) (empty)() (const), { + }, { + }, { + return (capacity_ == 0) || representation_.empty(); + }) + + /** If stack cannot accept any more item. */ + bool full() const + DBC_MEM_FUN( (public) (bool) (template)(Stack3) (full)() (const), { + }, { + }, { + return (capacity_ == 0) || representation_.full(); + }) + + // Element Change // + + /** Add x to top, othwerwise set error (no precondition). */ + void put(const T& x) + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(Stack3) + (put)( (const T&)(x) ), { + }, { + DBC_ASSERT(self.old.full() == (self.now.error() == OVERFLOW), + "error code if impossible"); + DBC_ASSERT(!self.old.full() == !self.now.error(), + "no error if possible"); + if (!self.now.error()) DBC_ASSERT(!self.now.empty(), + "not empty if no error"); + if (!self.now.error()) DBC_ASSERT(self.now.item() == x.now, + "added to top if no error"); + if (!self.now.error()) DBC_ASSERT( + self.now.count() == (self.old.count() + 1), + "one more item if no error"); + }, { + if (full()) { error_ = OVERFLOW; } + else { + representation_.put(x); + error_ = NONE; + } + }) + + /** Remove to element, othewise set error (no precondition). */ + void remove() + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(Stack3) (remove)(), { + }, { + DBC_ASSERT(self.old.empty() == (self.now.error() == UNDERFLOW), + "error code if impossible"); + DBC_ASSERT(!self.old.empty() == !self.now.error(), + "no error if possible"); + if (!self.now.error()) DBC_ASSERT(!self.now.full(), + "not full if no error"); + if (!self.now.error()) DBC_ASSERT( + self.now.count() == (self.old.count() - 1), + "one fewer item if no error"); + }, { + if (empty()) { error_ = UNDERFLOW; } + else { + representation_.remove(); + error_ = NONE; + } + }) + +private: + // Implementation // + + Stack4 representation_; + int capacity_; + mutable Error error_; // Changed by logically const op to report errors. + + DBC_INVARIANT(Stack3, {}) +}; + +#endif // STACK3_HPP_ + diff --git a/example/OO_SW_Construction/Stack3/main.cpp b/example/OO_SW_Construction/Stack3/main.cpp new file mode 100755 index 00000000..88900be6 --- /dev/null +++ b/example/OO_SW_Construction/Stack3/main.cpp @@ -0,0 +1,35 @@ +/* $Id$ */ + +#include "Stack3.hpp" +#include +#include + +int main() { + std::cout << std::endl << "init()..." << std::endl; + Stack3 s(3); + + std::cout << std::endl << "count()..." << std::endl; + std::cout << s.count() << std::endl; + + std::cout << std::endl << "empty()..." << std::endl; + std::cout << s.empty() << std::endl; + + std::cout << std::endl << "full()..." << std::endl; + std::cout << s.full() << std::endl; + + std::cout << std::endl << "put()..." << std::endl; + s.put("Galileo Galilei"); + + std::cout << std::endl << "capacity()..." << std::endl; + std::cout << s.capacity() << std::endl; + + std::cout << std::endl << "item()..." << std::endl; + std::cout << s.item() << std::endl; + + std::cout << std::endl << "remove()..." << std::endl; + s.remove(); + + std::cout << std::endl << "del()..." << std::endl; + return 0; +} + diff --git a/example/OO_SW_Construction/Stack4/Stack4.hpp b/example/OO_SW_Construction/Stack4/Stack4.hpp new file mode 100755 index 00000000..aa4cac11 --- /dev/null +++ b/example/OO_SW_Construction/Stack4/Stack4.hpp @@ -0,0 +1,156 @@ +/* $Id$ */ + +#ifndef STACK4_HPP_ +#define STACK4_HPP_ + +#include +#include + +/** + * Dispenser structures with a Last-In, First-Out access policy, and a fixed + * maximum capacity. + */ +template +class Stack4 DBC_INHERIT_OBJECT(Stack4) { +public: + // Initialization // + + /** Create stack for a maximum of n elements. */ + Stack4(const int& n): capacity_(0), count_(0), representation_() + { init(n); } // init() used to split implementation from declaration. + + /** Destroy this stack. (Just checks the invariant at entry.) */ + virtual ~Stack4() + DBC_DESTRUCTOR( (public) (virtual) (template)(Stack4)(), ; ) + + // Access // + + /** Maximum number of stack elements. */ + int capacity() const + DBC_MEM_FUN( (public) (int) (template)(Stack4) (capacity)() (const), { + }, { + }, ; ) + + /** Number of stack elements. */ + int count() const + DBC_MEM_FUN( (public) (int) (template)(Stack4) (count)() (const), { + }, { + }, ; ) + + /** Top element. */ + const T& item() const + DBC_MEM_FUN( (public) (const T&) (template)(Stack4) (item)() (const), { + DBC_ASSERT(!self.empty(), "not empty"); + }, { + }, ; ) + + // Status Report // + + /** If stack is empty. */ + bool empty() const + DBC_MEM_FUN( (public) (bool) (template)(Stack4) (empty)() (const), { + }, { + DBC_ASSERT(result == (self.now.count() == 0), + "result consistent with count"); + }, ; ) + + /** If stack is full. */ + bool full() const + DBC_MEM_FUN( (public) (bool) (template)(Stack4) (full)() (const), { + }, { + DBC_ASSERT(result == (self.now.count() == self.now.capacity()), + "result consistent with count"); + }, ; ) + + // Element Change // + + /** Add x on top. */ + void put(const T& x) + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(Stack4) + (put)( (const T&) (x) ), { + DBC_ASSERT(!self.full(), "not full"); + }, { + DBC_ASSERT(!self.now.empty(), "not empty"); + DBC_ASSERT(self.now.item() == x.now, "added to top"); + DBC_ASSERT(self.now.count() == (self.old.count() + 1), + "count increased"); + DBC_ASSERT(self.now.representation_.at(self.now.count() - 1) == x.now, + "at top array entry"); + }, ; ) + + /** Remove top item. */ + void remove() + DBC_MEM_FUN( (public) (void) (template)DBC_COPYABLE(Stack4) (remove)(), { + DBC_ASSERT(!self.empty(), "not empty"); + }, { + DBC_ASSERT(!self.now.full(), "not full"); + DBC_ASSERT(self.now.count() == (self.old.count() - 1), + "count decreased"); + }, ; ) + +private: + void init(const int& n) + DBC_CONSTRUCTOR( (private) (template)(Stack4)( (const int&) (n) ), { + DBC_ASSERT(n >= 0, "non negative capacity"); + }, { + DBC_ASSERT(self.now.capacity() == n.now, "capacity set"); + DBC_ASSERT(self.now.empty(), "is empty"); + }, ; ) + + int capacity_; + int count_; + std::vector representation_; + + DBC_INVARIANT(Stack4, { + DBC_ASSERT(self.count() >= 0, "count non negative"); + DBC_ASSERT_STREAM(self.count() <= self.capacity(), + "count no greather than capacity", + err << "count " << self.count() << " bounded by capacity " + << self.capacity()); + DBC_ASSERT( + self.capacity() == int(self.representation_.capacity()), + "capacity consistent with array capacity"); + DBC_ASSERT(self.empty() == (self.count() == 0), + "empty when no elements"); + if (self.count() > 0) DBC_ASSERT( + self.representation_.at(self.count() - 1) == self.item(), + "if positive count, item at top"); + }) +}; + +// Implementation // + +template +void Stack4::DBC_BODY(init)(const int& n) { + capacity_ = n; + representation_.resize(capacity_); +} + +template +DBC_DESTRUCTOR_BODY(Stack4)() {} + +template +int Stack4::DBC_BODY(capacity)() const { return capacity_; } + +template +int Stack4::DBC_BODY(count)() const { return count_; } + +template +const T& Stack4::DBC_BODY(item)() const + { return representation_.at(count() - 1); } + +template +bool Stack4::DBC_BODY(empty)() const { return count() == 0; } + +template +bool Stack4::DBC_BODY(full)() const { return count() == capacity(); } + +template +void Stack4::DBC_BODY(put)(const T& x) + { representation_.at(count_++) = x; } + +template +void Stack4::DBC_BODY(remove)() { --count_; } + +#endif // STACK4_HPP_ + diff --git a/example/OO_SW_Construction/Stack4/main.cpp b/example/OO_SW_Construction/Stack4/main.cpp new file mode 100755 index 00000000..11e85997 --- /dev/null +++ b/example/OO_SW_Construction/Stack4/main.cpp @@ -0,0 +1,35 @@ +/* $Id$ */ + +#include "Stack4.hpp" +#include +#include + +int main() { + std::cout << std::endl << "init()..." << std::endl; + Stack4 s(3); + + std::cout << std::endl << "capacity()..." << std::endl; + std::cout << s.capacity() << std::endl; + + std::cout << std::endl << "count()..." << std::endl; + std::cout << s.count() << std::endl; + + std::cout << std::endl << "put()..." << std::endl; + s.put("Galileo Galilei"); + + std::cout << std::endl << "empty()..." << std::endl; + std::cout << s.empty() << std::endl; + + std::cout << std::endl << "full()..." << std::endl; + std::cout << s.full() << std::endl; + + std::cout << std::endl << "item()..." << std::endl; + std::cout << s.item() << std::endl; + + std::cout << std::endl << "remove()..." << std::endl; + s.remove(); + + std::cout << std::endl << "del()..." << std::endl; + return 0; +} + diff --git a/example/README.txt b/example/README.txt new file mode 100755 index 00000000..29c82599 --- /dev/null +++ b/example/README.txt @@ -0,0 +1 @@ +A list of examples from various books and other sources. diff --git a/include/README.txt b/include/README.txt new file mode 100755 index 00000000..d09e4aa8 --- /dev/null +++ b/include/README.txt @@ -0,0 +1 @@ +C/C++ header files. diff --git a/include/dbc.hpp b/include/dbc.hpp new file mode 100755 index 00000000..c0ee87a8 --- /dev/null +++ b/include/dbc.hpp @@ -0,0 +1,27 @@ + +#ifndef DBC_HPP_ +#define DBC_HPP_ + +// Preprocessor #defines (must be included as 1st file). +#include "dbc/pp_define.hpp" + +// Configuration. +#include "dbc/config.hpp" +// Assertions. +#include "dbc/exception.hpp" +#include "dbc/assertion.hpp" +// Facilities. +#include "dbc/base.hpp" +#include "dbc/copyable.hpp" +#include "dbc/body.hpp" +#include "dbc/post.hpp" +#include "dbc/mparam.hpp" +// Contracts. +#include "dbc/object.hpp" +#include "dbc/invariant.hpp" +#include "dbc/fun.hpp" +#include "dbc/fun_macro.hpp" +#include "dbc/loop.hpp" + +#endif // DBC_HPP_ + diff --git a/include/dbc/README.txt b/include/dbc/README.txt new file mode 100755 index 00000000..b81059c1 --- /dev/null +++ b/include/dbc/README.txt @@ -0,0 +1,6 @@ +Design by Contract for C++ (DbC++) header files. + +DbC++ should be included only by #including "dbc.hpp" in parent directory. Do not include other header files separately. + +This directory contains library API files. +The "detail" directory contains library implementation files. diff --git a/include/dbc/assertion.hpp b/include/dbc/assertion.hpp new file mode 100755 index 00000000..432bf5b5 --- /dev/null +++ b/include/dbc/assertion.hpp @@ -0,0 +1,257 @@ + +#ifndef DBC_ASSERTION_HPP_ +#define DBC_ASSERTION_HPP_ + +#include "config.hpp" + +#if defined DBC_DOC + +#define DBC_ASSERT(condition, label) \ + DBC_CONFIG_DOC_ASSERTION(condition, label) +#define DBC_ASSERT_STREAM(condition, label, stream_error_code) \ + DBC_CONFIG_DOC_ASSERTION(condition, label) + +#elif !defined DBC + +#define DBC_ASSERT(condition, label) +#define DBC_ASSERT_STREAM(condition, label, stream_error_code) + +#else // DBC + +#include "exception.hpp" +#include "detail/logging.hpp" +#include +#include +#include + +#if DBC_CONFIG_DEFAULT_ON_ASSERTION_VIOLATION == \ + DBC_DEFAULT_RAISE_ON_ASSERTION_VIOLATION +# define DBC_ASSERTION_VIOLATION_ACTION_() raise() +#elif DBC_CONFIG_DEFAULT_ON_ASSERTION_VIOLATION == \ + DBC_DEFAULT_EXIT_ON_ASSERTION_VIOLATION +# define DBC_ASSERTION_VIOLATION_ACTION_() exit(1) +#elif DBC_CONFIG_DEFAULT_ON_ASSERTION_VIOLATION == \ + DBC_DEFAULT_TERMINATE_ON_ASSERTION_VIOLATION +# define DBC_ASSERTION_VIOLATION_ACTION_() terminate() +#endif + +#define DBC_ASSERT(condition, label) { \ + ::dbc::assertion(condition, label, __FILE__, __LINE__, \ + #condition).DBC_ASSERTION_VIOLATION_ACTION_(); \ +} + +#define DBC_ASSERT_STREAM(condition, label, stream_error_code) { \ + bool dbc_passed_ = (condition); \ + if (!dbc_passed_) { \ + /* This assertion always fails as cond is false. */ \ + ::dbc::oassertionstream err(false, label, __FILE__, __LINE__, \ + #condition); \ + /* User stream code might also throw, exit, terminate, etc. */ \ + { stream_error_code; } /* code only executed if false cond */ \ + /* This check always fails throwing the exception. */ \ + err.DBC_ASSERTION_VIOLATION_ACTION_(); \ + } else if (::dbc::log_level_ > ::dbc::LOG_LEVEL_VIOLATION) { \ + /* This assertion never fails as cond is true, but logs a msg. */ \ + ::dbc::oassertionstream(true, label, __FILE__, __LINE__, \ + #condition).DBC_ASSERTION_VIOLATION_ACTION_(); \ + } \ +} + +namespace dbc { + +class assertion { +public: + // Instance // + + explicit assertion(const bool& condition = true, + const std::string& label = "", + const char* file = "", const int& line = 0, + const std::string& code = ""): + condition_(condition), label_(label), + file_(file), line_(line), code_(code) {} + virtual ~assertion() {} + + // Accessors // + + virtual void condition(const bool& the_condition) + { condition_ = the_condition; } + virtual bool condition() const { return condition_; } + + virtual void label(const std::string& the_label) { label_ = the_label; } + virtual std::string label() const { return label_; } + + virtual void at(const char* file, const int& line) + { file_ = file; line_ = line; } + virtual void at(const std::string& file, const int& line) + { at(file.c_str(), line); } + virtual std::string file() const { return file_; } + virtual int line() const { return line_; } + + virtual void code(const std::string& the_code) { code_ = the_code; } + virtual std::string code() const { return code_; } + + // Checking // + + void raise() const { raise(); } + + template + void raise() const { + std::string msg = log(); + if (!DBC_CONFIG_ENABLE_THROWING_ASSERTIONS) { + DBC_LOG_DEBUG_(log << "Skipped disabled throwing assertion " + << msg); + } else if (!condition()) { + throw E(msg); + } + } + + template + void raise(const E& exception) const { + std::string msg = log(); + if (!DBC_CONFIG_ENABLE_THROWING_ASSERTIONS) { + DBC_LOG_DEBUG_(log << "Skipped disabled throwing assertion " + << msg); + } else if (!condition()) { + throw exception; + } + } + + void exit(const int& code) const { + std::string msg = log(); + if (!DBC_CONFIG_ENABLE_EXITING_ASSERTIONS) { + DBC_LOG_DEBUG_(log << "Skipped disabled exiting assertion " + << msg); + } else if (!condition()) { + DBC_LOG_VIOLATION_(log << "Exiting with code " << code + << " because: " << msg); + ::exit(code); + } + } + + void terminate() const { + std::string msg = log(); + if (!DBC_CONFIG_ENABLE_TERMINATING_ASSERTIONS) { + DBC_LOG_DEBUG_(log << "Skipped disabled terminating assertion " + << msg); + } else if (!condition()) { + DBC_LOG_VIOLATION_(log << "Terminating because: " << msg); + std::terminate(); + } + } + +protected: + virtual std::string what() const { + std::ostringstream oss; + oss << "Assertion"; + if ("" != label()) { oss << " \"" << label() << "\""; } + oss << (condition() ? " passed" : " failed"); + if ("" != code()) { oss << " as { " << code() << "; }"; } + if ("" != file()) { + oss << " at " << file(); + if (0 != line()) { oss << ":" << line(); } + } + return oss.str(); + } + +private: + std::string log() const { + std::string msg = what(); + if (!condition()) { DBC_LOG_VIOLATION_(log << msg); } + else { DBC_LOG_DEBUG_(log << msg); } + return msg; + } + + bool condition_; + std::string label_; + std::string file_; + int line_; + std::string code_; +}; + +// Streams // + +class oassertionstream: public assertion, public std::ostringstream { +public: + explicit oassertionstream(const bool& condition = true, + const std::string& label = "", + const char* file = "", const int& line = 0, + const std::string& code = ""): + assertion(condition, label, file, line, code), + std::ostringstream() {} + virtual ~oassertionstream() {} + +protected: + virtual std::string what() const { + std::ostringstream oss; + oss << "Assertion"; + if ("" != label()) { oss << " \"" << label() << "\""; } + oss << (condition() ? " passed" : " failed"); + std::string reason = str(); + if ("" != reason) { oss << " because \"" << reason << "\""; } + if ("" != code()) { oss << " as { " << code() << "; }"; } + if ("" != file()) { + oss << " at " << file(); + if (0 != line()) { oss << ":" << line(); } + } + return oss.str(); + } +}; + +/** Raise DBC default exceptions (will be automatically converted to + * precondition/postcondition/invariant_violation exception depending on where + * it was originally thrown from. */ +template +class raise { +public: + friend std::ostream& operator<<(std::ostream& s, const raise& o) { + oassertionstream& oas = dynamic_cast(s); + oas.raise(); + return oas; + } +}; + +/** Raise user specified exception (instead of DBC default one). */ +template +class uraise { +public: + explicit uraise(const E& exception): exception_(exception) {} + + friend std::ostream& operator<<(std::ostream& s, const uraise& o) { + oassertionstream& oas = dynamic_cast(s); + oas.raise(o.exception_); + return oas; + } + +private: + const E& exception_; +}; + +class exit { +public: + explicit exit(const int& code): code_(code) {} + + friend std::ostream& operator<<(std::ostream& s, const exit& o) { + oassertionstream& oas = dynamic_cast(s); + oas.exit(o.code_); + return oas; + } + +private: + const int code_; +}; + +class terminate { +public: + friend std::ostream& operator<<(std::ostream& s, const terminate& o) { + oassertionstream& oas = dynamic_cast(s); + oas.terminate(); + return oas; + } +}; + +} // namespace dbc + +#endif // DBC + +#endif // DBC_ASSERTION_HPP_ + diff --git a/include/dbc/base.hpp b/include/dbc/base.hpp new file mode 100755 index 00000000..3ce1b788 --- /dev/null +++ b/include/dbc/base.hpp @@ -0,0 +1,17 @@ + +#ifndef DBC_BASE_HPP_ +#define DBC_BASE_HPP_ + +#ifndef DBC + +#define DBC_BASE(base_class_type) (base_class_type) + +#else // DBC + +// Prefix must match symbol postfix of DBC_PP_BASE_dbc_base_ macro name. +#define DBC_BASE(base_class_type) (dbc_base_)(base_class_type) + +#endif // DBC + +#endif // DBC_BASE_HPP_ + diff --git a/include/dbc/body.hpp b/include/dbc/body.hpp new file mode 100755 index 00000000..4af3e928 --- /dev/null +++ b/include/dbc/body.hpp @@ -0,0 +1,38 @@ + +#ifndef DBC_BODY_HPP_ +#define DBC_BODY_HPP_ + +#ifndef DBC + +#define DBC_BODY(fun_name) fun_name +#define DBC_OPERATOR_BODY(operator_symbol, operator_name) operator_symbol +#define DBC_DESTRUCTOR_BODY(class_type) class_type :: ~class_type + +#else // DBC + +#include + +#define DBC_BODY(fun_name) BOOST_PP_CAT(BOOST_PP_CAT(dbc_body_, fun_name), _) + +/** Needed because operator_symbol cannot in token concatenation when creating + * contract name. */ +#define DBC_OPERATOR_BODY(operator_symbol, operator_name) \ + DBC_BODY(operator_name) + +// Unfortunately, it is not possible to implement a macro +// DBC_CONSTRUCTOR_BODY() (similar to the destructor macro below) that can deal +// with base initializers in a reasonable way. Such a constructor body macro +// would need the full function signature as specified to DBC_CONSTRUCTOR() in +// the constructor contract specification. It would be too inconvenient for the +// user to specify such complex signature twice. Furthermore, templates present +// another major issue as it seems that a constructor body macro would require +// the full template signature... If C++ suppoerted delegating constructors, +// there could have been a way around these issues. +// To split constructor implementation from declaration, simply use init(). + +#define DBC_DESTRUCTOR_BODY(class_type) void class_type :: DBC_BODY(del) + +#endif // DBC + +#endif // DBC_BODY_HPP_ + diff --git a/include/dbc/config.hpp b/include/dbc/config.hpp new file mode 100755 index 00000000..2a6033c0 --- /dev/null +++ b/include/dbc/config.hpp @@ -0,0 +1,132 @@ + +#ifndef DBC_CONFIG_HPP_ +#define DBC_CONFIG_HPP_ + +// Following must be present regardless of DBC #definition. + +#include + +// Assertion // + +/** Whether assertions that throw are checked or not (default: true). */ +#ifndef DBC_CONFIG_ENABLE_THROWING_ASSERTIONS +# define DBC_CONFIG_ENABLE_THROWING_ASSERTIONS true +#endif + +/** Whether assertions that exit are checked or not (default: true). */ +#ifndef DBC_CONFIG_ENABLE_EXITING_ASSERTIONS +# define DBC_CONFIG_ENABLE_EXITING_ASSERTIONS true +#endif + +/** Whether assertions that terminate are checked or not (default: true). */ +#ifndef DBC_CONFIG_ENABLE_TERMINATING_ASSERTIONS +# define DBC_CONFIG_ENABLE_TERMINATING_ASSERTIONS true +#endif + +/** Default action on assertion violation: raise(), exit(1), or terminate(). */ +#define DBC_DEFAULT_RAISE_ON_ASSERTION_VIOLATION 0 +#define DBC_DEFAULT_EXIT_ON_ASSERTION_VIOLATION 1 +#define DBC_DEFAULT_TERMINATE_ON_ASSERTION_VIOLATION 2 +#ifndef DBC_DEFAULT_DEFAULT_ON_ASSERTION_VIOLATION +# define DBC_CONFIG_DEFAULT_ON_ASSERTION_VIOLATION \ + DBC_DEFAULT_RAISE_ON_ASSERTION_VIOLATION +#endif +#ifdef DBC // Validate macro value only if DBC. +# if DBC_CONFIG_DEFAULT_ON_ASSERTION_VIOLATION != \ + DBC_DEFAULT_RAISE_ON_ASSERTION_VIOLATION && \ + DBC_CONFIG_DEFAULT_ON_ASSERTION_VIOLATION != \ + DBC_DEFAULT_EXIT_ON_ASSERTION_VIOLATION && \ + DBC_CONFIG_DEFAULT_ON_ASSERTION_VIOLATION != \ + DBC_DEFAULT_TERMINATE_ON_ASSERTION_VIOLATION +# error "invalid DBC default action on assertion violation DBC_CONFIG_DEFAULT_ON_ASSERTION_VIOLATION" +# endif +#endif //DBC + +// Logging // + +/** Log level NONE, VIOLATION (default), DEBUG, ALL. */ +#define DBC_LOG_LEVEL_NONE 0 +#define DBC_LOG_LEVEL_VIOLATION 1 +#define DBC_LOG_LEVEL_DEBUG 2 +#define DBC_LOG_LEVEL_ALL 3 +#ifndef DBC_CONFIG_LOG_LEVEL +# define DBC_CONFIG_LOG_LEVEL DBC_LOG_LEVEL_VIOLATION +#endif +#ifdef DBC // Validate macro value only if DBC. +# if DBC_CONFIG_LOG_LEVEL != DBC_LOG_LEVEL_NONE && \ + DBC_CONFIG_LOG_LEVEL != DBC_LOG_LEVEL_VIOLATION && \ + DBC_CONFIG_LOG_LEVEL != DBC_LOG_LEVEL_DEBUG && \ + DBC_CONFIG_LOG_LEVEL != DBC_LOG_LEVEL_ALL +# error "invalid DBC log level DBC_CONFIG_LOG_LEVEL" +# endif +#endif //DBC + +/** + * Print to log specified debug message (default to std::clog). + * @param[in] message C-style null-terminated message string. + */ +#ifndef DBC_CONFIG_LOG_DEBUG +# define DBC_CONFIG_LOG_DEBUG(message) \ + { std::clog << "dbc: " << message << std::endl; } +#endif + +/** + * Print to log specified assertion violation message (default to std::cerr). + * @param[in] message C-style null-terminated message string. + */ +#ifndef DBC_CONFIG_LOG_VIOLATION +# define DBC_CONFIG_LOG_VIOLATION(message) \ + { std::cerr << "dbc: " << message << std::endl; } +#endif + +// Documentation // + +/** Document comment for assertion. */ +#ifndef DBC_CONFIG_DOC_ASSERTION + // The comment "/*label*/" below prints label in Doxygen (don't change it). +# define DBC_CONFIG_DOC_ASSERTION(condition, label) condition/*label*/ +#endif + +/** Documentation comment for precoditions code block. */ +#ifndef DBC_CONFIG_DOC_REQUIRE +# define DBC_CONFIG_DOC_REQUIRE(require_code) /** \pre \code require_code \endcode */ +#endif + +/** Documentation comment for postcondition code block. */ +#ifndef DBC_CONFIG_DOC_ENSURE +# define DBC_CONFIG_DOC_ENSURE(ensure_code) /** \post \code ensure_code \endcode */ +#endif + +/** Documentation comment for class invariant code block. */ +#ifndef DBC_CONFIG_DOC_INVARIANT +# define DBC_CONFIG_DOC_INVARIANT(full_class_type, invariant_code) \ + /**
Precondition/Postcondition/Invariant Parameters:
       
[in]selfConstant reference to this object (similar to \c *this in \c const context).
[in]entity.oldValue \e entity had when preconditions were evaluated (in postcondition and for copiable \e entity only).
[in]entity.nowValue \e entity has when postconditions are evaluated (in postcondition only).
[in]resultValue being returned (in postcondition of non-void functions only).
*/ \ + /** \invariant */ \ + /** \code invariant_code \endcode */ \ + /** \class full_class_type */ +#endif + +// Library Compilation // + +/** Maximum number of argument supported for contract function (5 default). + * Increasing this number will significantly increase compilation time! */ +#ifndef DBC_CONFIG_MAX_ARGC +# define DBC_CONFIG_MAX_ARGC 3 // default max arg count +#endif +#ifdef DBC // Validate macro value only if DBC. +# if DBC_CONFIG_MAX_ARGC < 0 +# error "DBC argument count DBC_CONFIG_MAX_ARGC count be negative" +# endif +# include // Included only if DBC. +# if DBC_CONFIG_MAX_ARGS > BOOST_PP_LIMIT_MAG +# error "DBC argument count DBC_CONFIG_MAX_ARGC cannot be greater then BOOST_PP_LIMIT_MAG" +# endif +#endif // DBC + +/** This files path must exist within your include path settings (-I). */ +#ifndef DBC_CONFIG_FUN_HPP_FILE_PATH +# define DBC_CONFIG_FUN_HPP_FILE_PATH "dbc/fun.hpp" +#endif + +#endif // DBC_CONFIG_HPP_ + diff --git a/include/dbc/copyable.hpp b/include/dbc/copyable.hpp new file mode 100755 index 00000000..d707bbd2 --- /dev/null +++ b/include/dbc/copyable.hpp @@ -0,0 +1,24 @@ + +#ifndef DBC_COPYABLE_HPP_ +#define DBC_COPYABLE_HPP_ + +#ifndef DBC + +#define DBC_COPYABLE(type) (type) + +#else // DBC + +// Prefix must match symbol postfix of DBC_PP_BASE_dbc_copyable_ macro name. +#define DBC_COPYABLE(type) (dbc_copyable_)(type) + +namespace dbc { + +/** Tag specified type as copyable. */ +template struct copyable { typedef T type; }; + +} // namespace dbc + +#endif // DBC + +#endif // DBC_COPYABLE_HPP_ + diff --git a/include/dbc/detail/checking.hpp b/include/dbc/detail/checking.hpp new file mode 100755 index 00000000..41c88e2e --- /dev/null +++ b/include/dbc/detail/checking.hpp @@ -0,0 +1,19 @@ + +#ifndef DBC_CHECKING_HPP_ +#define DBC_CHECKING_HPP_ + +#include "threading/sync.hpp" + +namespace dbc { + +/** True iff is checking a contract within any object. */ +/** @todo[LC] Does this effectivelly sync all objects within the systems since + * it is a global lock? If there any way around this? This is required from a + * DBC checking policy "assertions are (globally) disabled within + * assertions"... */ +static sync_ globally_checking_contract_; + +} // namespace dbc + +#endif // DBC_CHECKING_HPP_ + diff --git a/include/dbc/detail/fun_macro_traits.hpp b/include/dbc/detail/fun_macro_traits.hpp new file mode 100755 index 00000000..74d508b1 --- /dev/null +++ b/include/dbc/detail/fun_macro_traits.hpp @@ -0,0 +1,120 @@ + +#ifndef DBC_FUN_MACRO_TRATIS_HPP_ +#define DBC_FUN_MACRO_TRATIS_HPP_ + +#include "../post.hpp" +#include "pp/fun_traits.hpp" +#include "type_traits.hpp" +#include + +// Argument names. + +#define DBC_FUN_GET_ARG_NAME_OP_(z, n, f) \ + DBC_PP_FUN_GET_ARG_NAME_(DBC_PP_FUN_GET_ARG_(n, f)) + +#define DBC_FUN_GET_TRAILING_ARG_NAMES_(f) \ + BOOST_PP_COMMA_IF(DBC_PP_FUN_HAS_ARGS_(f)) \ + BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(DBC_PP_FUN_GET_ARGS_(f)), \ + DBC_FUN_GET_ARG_NAME_OP_, f) + +// Argument types. + +#define DBC_FUN_GET_ARG_TYPE_OP_(z, n, f) \ + DBC_PP_FUN_GET_ARG_COPYABLE_TYPE_(DBC_PP_FUN_GET_ARG_(n, f)) + +#define DBC_FUN_GET_ARG_TYPES_(f) \ + BOOST_PP_COMMA_IF(DBC_PP_FUN_HAS_ARGS_(f)) \ + BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(DBC_PP_FUN_GET_ARGS_(f)), \ + DBC_FUN_GET_ARG_TYPE_OP_, f) + +// Body's arguments. + +#define DBC_FUN_GET_BODY_ARG_OP_(z, n, f) \ + DBC_PP_FUN_GET_ARG_TYPE_(DBC_PP_FUN_GET_ARG_(n, f)) \ + DBC_PP_FUN_GET_ARG_NAME_(DBC_PP_FUN_GET_ARG_(n, f)) + +#define DBC_FUN_GET_BODY_ARGS_(f) \ + BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(DBC_PP_FUN_GET_ARGS_(f)), \ + DBC_FUN_GET_BODY_ARG_OP_, f) + +// Require's arguments. + +#define DBC_FUN_GET_MEM_REQUIRE_ARG_OP_(z, n, f) \ + DBC_ADD_CONST_REF_(DBC_PP_FUN_GET_TYPENAME_(f), \ + DBC_PP_FUN_GET_ARG_TYPE_(DBC_PP_FUN_GET_ARG_(n, f))) \ + DBC_PP_FUN_GET_ARG_NAME_(DBC_PP_FUN_GET_ARG_(n, f)) + +#define DBC_FUN_GET_MEM_REQUIRE_ARGS_(f) \ + /* self */ \ + DBC_ADD_CONST_REF_(DBC_PP_FUN_GET_TYPENAME_(f), \ + DBC_PP_FUN_GET_CLASS_TYPE_(f)) \ + self \ + /* args (if any) */ \ + BOOST_PP_COMMA_IF(DBC_PP_FUN_HAS_ARGS_(f)) \ + BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(DBC_PP_FUN_GET_ARGS_(f)), \ + DBC_FUN_GET_MEM_REQUIRE_ARG_OP_, f) + +// Ensure's arguments. + +#define DBC_FUN_GET_MEM_ENSURE_ARG_OP_(z, n, f) \ + DBC_ADD_POST_CONST_REF_(DBC_PP_FUN_GET_TYPENAME_(f), \ + DBC_PP_FUN_GET_ARG_COPYABLE_TYPE_( \ + DBC_PP_FUN_GET_ARG_(n, f))) \ + DBC_PP_FUN_GET_ARG_NAME_(DBC_PP_FUN_GET_ARG_(n, f)) + +#define DBC_FUN_GET_MEM_ENSURE_ARGS_(f) \ + /* post<> self */ \ + DBC_ADD_POST_CONST_REF_(DBC_PP_FUN_GET_TYPENAME_(f), \ + DBC_PP_FUN_GET_CONST_CLASS_COPYABLE_TYPE_(f)) \ + self \ + /* post<> args (if any) */ \ + BOOST_PP_COMMA_IF(DBC_PP_FUN_HAS_ARGS_(f)) \ + BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(DBC_PP_FUN_GET_ARGS_(f)), \ + DBC_FUN_GET_MEM_ENSURE_ARG_OP_, f) \ + /* result (if non-void) */ \ + BOOST_PP_COMMA_IF(BOOST_PP_NOT(DBC_PP_FUN_IS_VOID_(f))) \ + BOOST_PP_IF(DBC_PP_FUN_IS_VOID_(f), BOOST_PP_EMPTY(), \ + DBC_ADD_CONST_REF_(DBC_PP_FUN_GET_TYPENAME_(f), \ + DBC_PP_FUN_GET_RESULT_TYPE_(f)) result) + +// Contract name. + +/** Postfix arg-names allow to somewhat handle fun overloading. */ +#define DBC_FUN_GET_CONTRACT_NAME_OP_(s, state, arg) \ + BOOST_PP_CAT(state, BOOST_PP_CAT(DBC_PP_FUN_GET_ARG_NAME_(arg), _)) + +#define DBC_FUN_GET_CONTRACT_NAME_(f) \ + BOOST_PP_CAT(dbc_contract_, \ + BOOST_PP_CAT(DBC_PP_FUN_GET_NAME_(f), BOOST_PP_CAT(_, \ + BOOST_PP_IF(BOOST_PP_SEQ_SIZE(DBC_PP_FUN_GET_ARGS_(f)), \ + BOOST_PP_SEQ_FOLD_LEFT, BOOST_PP_TUPLE_EAT(3) \ + )(DBC_FUN_GET_CONTRACT_NAME_OP_, , DBC_PP_FUN_GET_ARGS_(f)) \ + ))) \ + +// Contract type. + +#define DBC_FUN_GET_MEM_CONTRACT_TYPE_(f, inner_contract_type) \ + ::dbc::fun< DBC_PP_FUN_GET_RESULT_TYPE_(f) \ + DBC_FUN_GET_ARG_TYPES_(f) >:: DBC_PP_FUN_GET_TEMPLATE_(f) \ + inner_contract_type< DBC_PP_FUN_GET_CONST_CLASS_COPYABLE_TYPE_(f) \ + /* base contract (if any) */ \ + BOOST_PP_COMMA_IF(DBC_PP_FUN_IS_DERIVED_(f)) \ + BOOST_PP_IF(DBC_PP_FUN_IS_DERIVED_(f), \ + DBC_PP_FUN_GET_TYPENAME_(f) \ + DBC_PP_FUN_GET_BASE_TYPE_(f) \ + :: DBC_FUN_GET_CONTRACT_NAME_(f) \ + , /* else (no base contract) */ \ + BOOST_PP_EMPTY()) \ + > \ + BOOST_PP_IF(DBC_PP_FUN_IS_TEMPLATE_(f), ::this_type, BOOST_PP_EMPTY()) + +// Base contract type. + +#define DBC_FUN_GET_MEM_BASE_CONTRACT_TYPE_(f) \ + BOOST_PP_IF(DBC_PP_FUN_IS_DERIVED_(f), \ + DBC_PP_FUN_GET_TYPENAME_(f) DBC_PP_FUN_GET_BASE_TYPE_(f) \ + :: DBC_FUN_GET_CONTRACT_NAME_(f) ::class_type \ + , BOOST_PP_EMPTY()) + +#endif // DBC_FUN_MACRO_TRATIS_HPP_ + diff --git a/include/dbc/detail/logging.hpp b/include/dbc/detail/logging.hpp new file mode 100755 index 00000000..273cd13e --- /dev/null +++ b/include/dbc/detail/logging.hpp @@ -0,0 +1,47 @@ + +#ifndef DBC_LOG_HPP_ +#define DBC_LOG_HPP_ + +#include "../config.hpp" +#include + +/** + * Print log message when equal or more verbose than LOG_LEVEL_VIOLATION. + * @param code Declare loval variable "log" that can be set by specified code. + * Uses DBC_CONF_LOG_VIOLATION() to print log message. + */ +#define DBC_LOG_VIOLATION_(code) \ + if (::dbc::log_level_ >= ::dbc::LOG_LEVEL_VIOLATION) { \ + std::ostringstream log; \ + { code; } \ + DBC_CONFIG_LOG_VIOLATION(log.str().c_str()); \ + } + +/** + * Print log message when equal or more verbose than LOG_LEVEL_DEBUG. + * @param code Declare loval variable "log" that can be set by specified code. + * Uses DBC_CONF_LOG_DEBUG() to print log message. + */ +#define DBC_LOG_DEBUG_(code) \ + if (::dbc::log_level_ >= ::dbc::LOG_LEVEL_DEBUG) { \ + std::ostringstream log; \ + { code; } \ + DBC_CONFIG_LOG_DEBUG(log.str().c_str()); \ + } + +namespace dbc { + +enum log_level_type_ { + // Level definition order and start from 0 matters (actual values don't). + LOG_LEVEL_NONE = DBC_LOG_LEVEL_NONE, + LOG_LEVEL_VIOLATION = DBC_LOG_LEVEL_VIOLATION, + LOG_LEVEL_DEBUG = DBC_LOG_LEVEL_DEBUG, + LOG_LEVEL_ALL = DBC_LOG_LEVEL_ALL, +}; + +static log_level_type_ log_level_ = log_level_type_(DBC_CONFIG_LOG_LEVEL); + +} // namespace dbc + +#endif // DBC_LOG_HPP_ + diff --git a/include/dbc/detail/none.hpp b/include/dbc/detail/none.hpp new file mode 100755 index 00000000..8bf3f3eb --- /dev/null +++ b/include/dbc/detail/none.hpp @@ -0,0 +1,14 @@ + +#ifndef DBC_NONE_HPP_ +#define DBC_NONE_HPP_ + +namespace dbc { + +/** @todo[LC] Can I use boost::none and/or boost::mpl::empty instead of my own + * none_ ? */ +class none_ {}; + +} // namespace dbc + +#endif // DBC_NONE_HPP_ + diff --git a/include/dbc/detail/pp/fun.hpp b/include/dbc/detail/pp/fun.hpp new file mode 100755 index 00000000..fcdfa340 --- /dev/null +++ b/include/dbc/detail/pp/fun.hpp @@ -0,0 +1,134 @@ + +#ifndef DBC_PP_FUN_HPP_ +#define DBC_PP_FUN_HPP_ + +#include "fun_parse/access.hpp" +#include "fun_parse/virtual.hpp" +#include "fun_parse/result_type.hpp" +#include "fun_parse/class_type.hpp" +#include "fun_parse/base_type.hpp" +#include "fun_parse/name.hpp" +#include "fun_parse/args.hpp" +#include "fun_parse/const.hpp" +#include "fun_parse/util/validate.hpp" +#include "fun_parse/util/match_not.hpp" +#include "fun_parse/util/append_traits.hpp" +#include "keywords.hpp" +#include "fun_traits.hpp" +#include + +/** + * Parse specified member function signature. + * Different function traits can be inspected from this parse result using + * the macros in file "pp/fun_traits.hpp". + * @param signature Boost.Preprocessor sequence representing the member + * function signature in the following format: + * {(private)||(protected)||(public)} [(virtual)] (result_type) + * [(template)][DBC_COPYABLE](class_type)[DBC_BASE(base_class_type)] + * (name)( {[DBC_COPYABLE](arg_type) (arg_name)}* ) + * [(const)] + * Where: () wrap a Boost.Preprocessor sequence token (they must be used). + * [] indicate an optional expression. + * {} is the result of expression within parenthesis. + * || the "or" logical operator. + * * preceding expression repeated 0 or more times. + * result_type are any valid type identifier. + * DBC_COPIABLE(type) indicate specified type has copy contructor. + * Spaces can be used more or less freely. + * @return Boost.Preprocessor tuple of 3 elements "(remaining-signature, + * traits, (error-code, error-message))". "remaining-signature" is the + * sequence of signature token that are still left to parse (it should be an + * empty sequence unless there was an error). "traits" are the function traits + * (or attributes) as parsed from the signature, they can be inspected using + * the macros in fun_tratis.hpp (the tratis sequence may not be complete in + * case of error, so you should use it only if error-code is 0). "(error-code, + * error-message)" is in turn another tuple of 2-elements that where + * "error-code" is 0 only if there was no error and "error-message" is a + * C-style (null terminated) string containing the error description. + */ +#define DBC_PP_MEM_FUN_(signature) \ + /* the order of the following matters -- don't change it! */ \ + DBC_PP_FUN_PARSE_VALIDATE_(DBC_PP_FUN_TRAITS_COUNT_, \ + DBC_PP_FUN_PARSE_CONST_( /* [(const)] */ \ + DBC_PP_FUN_PARSE_ARGS_(DBC_PP_FUN_PARSE_ARGS_INFINITY_, \ + /* {[DBC_COPYABLE](arg_type) (arg_name)}* ) */ \ + DBC_PP_FUN_PARSE_NAME_( /* (name) */ \ + DBC_PP_FUN_PARSE_BASE_TYPE_( /* [DBC_BASE(base_type)] */\ + DBC_PP_FUN_PARSE_CLASS_TYPE_( /* [(template)][DBC_COPYABLE](class_type) */\ + DBC_PP_FUN_PARSE_RESULT_TYPE_( /* (result_type) */ \ + DBC_PP_FUN_PARSE_VIRTUAL_( /* [(virtual)] */ \ + DBC_PP_FUN_PARSE_ACCESS_( /* {(private)||(protected)||(public)} */\ + /* init sign_attr_err 3-elem tuple */ \ + ( signature /* remaining fun signature to parse */, \ + /* parsed fun attributes (empty sequence to start) */, \ + (0, "") /* error (code, message) */ ) \ + ))))))))) + +/** + * Parse specified constructor signature. + * @param signature Boost.Preprocessor sequence representing the constructor + * function signature in the following format: + * {(private)||(protected)||(public)} + * [(template)](class_type) + * ( {[DBC_COPYABLE](arg_type) (arg_name)}* ) + * Note that respect to member functions, constructor signatures have no + * virtual, no result_type, class_type cannot be copyable, no base_class_type, + * no name, no const qualifier (as from C++ constructors declaration syntax). + * @see DBC_PP_MEM_FUN() + */ +#define DBC_PP_CONSTRUCTOR_(signature) \ + DBC_PP_FUN_PARSE_VALIDATE_(DBC_PP_FUN_TRAITS_COUNT_, \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (/* enforce no const */), \ + DBC_PP_FUN_PARSE_MATCH_NOT_(DBC_PP_IS_CONST_, \ + DBC_ERROR_constructors_may_not_be_cv_qualified_, \ + DBC_PP_FUN_PARSE_ARGS_(DBC_PP_FUN_PARSE_ARGS_INFINITY_, \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (init) /* enforce "init" for name */, \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (/* no base */), \ + DBC_PP_FUN_PARSE_UNCOPYABLE_CLASS_TYPE_( \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (void) /* void enforce no result */, \ + DBC_PP_FUN_PARSE_MATCH_NOT_(DBC_PP_IS_VOID_, \ + DBC_ERROR_constructor_cannot_have_return_value_not_even_void_, \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (/* enforce no virtual */), \ + DBC_PP_FUN_PARSE_MATCH_NOT_(DBC_PP_IS_VIRTUAL_, \ + DBC_ERROR_return_type_specification_for_constructor_invalid_, \ + DBC_PP_FUN_PARSE_ACCESS_( \ + /* init sign_attr_err 3-elem tuple */ \ + ( signature /* remaining fun signature to parse */, \ + /* parsed fun attributes (empty sequence to start) */, \ + (0, "") /* error (code, message) */ ) \ + )))))))))))) + +/** + * Parse specified destructor signature. + * @param signature Boost.Preprocessor sequence representing the destructor + * function signature in the following format: + * {(private)||(protected)||(public)} [(virtual)] + * [(template)](class_type) + * () + * Note that respect to member functions, destructor signatures have no + * result_type, class_type cannot be copyable, no name, empty argument list, + * no const qualifier (as from C++ destructors declaration syntax). + * @see DBC_PP_MEM_FUN() + */ +#define DBC_PP_DESTRUCTOR_(signature) \ + DBC_PP_FUN_PARSE_VALIDATE_(DBC_PP_FUN_TRAITS_COUNT_, \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (/* enforce no const */), \ + DBC_PP_FUN_PARSE_MATCH_NOT_(DBC_PP_IS_CONST_, \ + DBC_ERROR_destructors_may_not_be_cv_qualified_, \ + DBC_PP_FUN_PARSE_ARGS_(0 /* no args, must use () */, \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (del) /* enforce "del" for name */, \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (/* no base */), \ + DBC_PP_FUN_PARSE_UNCOPYABLE_CLASS_TYPE_( \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_( (void) /* void enforce no result */, \ + DBC_PP_FUN_PARSE_MATCH_NOT_(DBC_PP_IS_VOID_, \ + DBC_ERROR_return_type_specification_for_destructor_invalid_, \ + DBC_PP_FUN_PARSE_VIRTUAL_( \ + DBC_PP_FUN_PARSE_ACCESS_( \ + /* init sign_attr_err 3-elem tuple */ \ + ( signature /* remaining fun signature to parse */, \ + /* parsed fun attributes (empty sequence to start) */, \ + (0, "") /* error (code, message) */ ) \ + ))))))))))) + +#endif // DBC_PP_FUN_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/access.hpp b/include/dbc/detail/pp/fun_parse/access.hpp new file mode 100755 index 00000000..c24ca5c9 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/access.hpp @@ -0,0 +1,46 @@ + +#ifndef DBC_PP_FUN_PARSE_ACCESS_HPP_ +#define DBC_PP_FUN_PARSE_ACCESS_HPP_ + +#include "../keywords.hpp" +#include "util/apply.hpp" +#include "util/returns.hpp" +#include + +#define DBC_PP_FUN_PARSE_ACCESS_OP_(sign_traits_err, data) /**/ \ + BOOST_PP_IF(DBC_PP_IS_PRIVATE_(BOOST_PP_SEQ_HEAD( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))), \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_SEQ_TAIL(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)),\ + (private)) \ + , BOOST_PP_IF(DBC_PP_IS_PROTECTED_(BOOST_PP_SEQ_HEAD( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))), \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_SEQ_TAIL(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)),\ + (protected)) \ + , BOOST_PP_IF(DBC_PP_IS_PUBLIC_(BOOST_PP_SEQ_HEAD( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))), \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_SEQ_TAIL(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)),\ + (public)) \ + , /* else (error) */ \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_SEQ_TAIL(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)),\ + (/* no valid access given */), \ + /* The invalid token is BOOST_PP_STRINGIZE(BOOST_PP_SEQ_HEAD(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))) */ \ + DBC_ERROR_given_invalid_access_specifier_where_private_protected_or_public_expected_) \ + ))) /* endif */ \ + /**/ + +#define DBC_PP_FUN_PARSE_ACCESS_NO_(sign_traits_err, data) \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + (/* no valid access given */), \ + DBC_ERROR_missing_access_specifier_where_private_protected_or_public_expected_) + +#define DBC_PP_FUN_PARSE_ACCESS_(sign_traits_err) \ + DBC_PP_FUN_PARSE_APPLY_(DBC_PP_FUN_PARSE_ACCESS_OP_, \ + DBC_PP_FUN_PARSE_ACCESS_NO_, sign_traits_err, ~) + +#endif // DBC_PP_FUN_PARSE_ACCESS_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/args.hpp b/include/dbc/detail/pp/fun_parse/args.hpp new file mode 100755 index 00000000..ebb21cd4 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/args.hpp @@ -0,0 +1,138 @@ + +#ifndef DBC_PP_FUN_PARSE_ARGS_HPP_ +#define DBC_PP_FUN_PARSE_ARGS_HPP_ + +#include "util/required.hpp" +#include + +#define DBC_PP_FUN_PARSE_ARGS_INFINITY_ BOOST_PP_LIMIT_MAG + +// Non-Copyable Arg // + +#define DBC_PP_FUN_PARSE_NONCOPYABLE_ARG_OP_(sign_traits_err_for_args) /**/ \ + /* got here only is argsign seq size >= 2 */ \ + BOOST_PP_IF(BOOST_PP_IS_EMPTY(BOOST_PP_SEQ_ELEM(0, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args))), \ + ( , , (1, DBC_ERROR_given_empty_function_argument_type_ /* in BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)) */) ) \ + , BOOST_PP_IF(BOOST_PP_IS_EMPTY(BOOST_PP_SEQ_ELEM(1, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args))), \ + ( , , (1, DBC_ERROR_given_empty_function_argument_name_ /* in BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)) */) ) \ + , /* else */ \ + ( /* remove first 2 (type)(name) and continue */ \ + BOOST_PP_SEQ_REST_N(2, BOOST_PP_TUPLE_ELEM(3, 0, \ + sign_traits_err_for_args)) \ + , BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err_for_args) /*seq concat*/\ + ( BOOST_PP_SEQ_TO_TUPLE( /* return (, type, name) */ \ + (/* empty seq elem for non-copyable */) \ + BOOST_PP_SEQ_SUBSEQ(BOOST_PP_TUPLE_ELEM(3, 0, \ + sign_traits_err_for_args), 0, 2)) ) /* (type)(name) */\ + , (0, "") /* no error */ ) \ + )) /* end if */ \ + /**/ + +#define DBC_PP_FUN_PARSE_NONCOPYABLE_ARG_(sign_traits_err_for_args) /**/ \ + BOOST_PP_IF(BOOST_PP_LESS(BOOST_PP_SEQ_SIZE( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)), 2), \ + ( , , (1, DBC_ERROR_missing_function_argument_type_or_name_ /* but only these given BOOST_PP_STRINGIZE(BOOST_PP_SEQ_SIZE(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args))) "given as" BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args))*/) ) \ + BOOST_PP_TUPLE_EAT(1) \ + , /* else */ \ + DBC_PP_FUN_PARSE_NONCOPYABLE_ARG_OP_ \ + )(sign_traits_err_for_args) /* end if */ + +// Copyable Arg // + +#define DBC_PP_FUN_PARSE_COPYABLE_ARG_OP_(sign_traits_err_for_args) /**/ \ + /* got here only is argsign seq size >= 3 (elem 0 is (dbc_copyable_) */ \ + /* SEQ_TAIL used below to hide dbc_copyable_ from use error msg */ \ + BOOST_PP_IF(BOOST_PP_IS_EMPTY(BOOST_PP_SEQ_ELEM(1, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args))), \ + ( , , (1, DBC_ERROR_given_empty_funtion_argument_type_ /* in BOOST_PP_STRINGIZE(BOOST_PP_SEQ_TAIL(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)))*/) ) \ + , BOOST_PP_IF(BOOST_PP_IS_EMPTY(BOOST_PP_SEQ_ELEM(2, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args))), \ + ( , , (1, DBC_ERROR_given_empty_funtion_argument_name_ /* in BOOST_PP_STRINGIZE(BOOST_PP_SEQ_TAIL(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)))*/) ) \ + , /* else */ \ + ( /* remove first 3 (copyable)(type)(name) and continue */ \ + BOOST_PP_SEQ_REST_N(3, BOOST_PP_TUPLE_ELEM(3, 0, \ + sign_traits_err_for_args)) \ + , BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err_for_args) /*seq concat*/ \ + ( BOOST_PP_SEQ_TO_TUPLE( /* return (copyable, type, name) */ \ + (dbc_copyable_) /* copyable */ \ + BOOST_PP_SEQ_SUBSEQ(BOOST_PP_TUPLE_ELEM(3, 0, \ + sign_traits_err_for_args), 1, 2)) ) /* (type)(name) */ \ + , (0, "") /* no error */ ) \ + )) /* end if */ \ + /**/ + +#define DBC_PP_FUN_PARSE_COPYABLE_ARG_(sign_traits_err_for_args) /**/ \ + BOOST_PP_IF(BOOST_PP_LESS(BOOST_PP_SEQ_SIZE( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)), 3), \ + ( , , (1, DBC_ERROR_missing_function_argument_type_or_name_for_copyable_type_ /* but only these given BOOST_PP_STRINGIZE(BOOST_PP_SUB(BOOST_PP_SEQ_SIZE(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)), 1)) "given as" BOOST_PP_STRINGIZE(BOOST_PP_SEQ_TAIL(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)))*/) ) \ + BOOST_PP_TUPLE_EAT(1) \ + , /* else */ \ + DBC_PP_FUN_PARSE_COPYABLE_ARG_OP_ \ + )(sign_traits_err_for_args) /* end if */ + +// Loop // + +#define DBC_PP_FUN_PARSE_ARGS_LOOP_PRED_(d, sign_traits_err_for_args) \ + BOOST_PP_SEQ_SIZE(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args)) + +#define DBC_PP_FUN_PARSE_ARGS_LOOP_OP_(d, sign_traits_err_for_args) \ + BOOST_PP_IF(DBC_PP_IS_COPYABLE_(BOOST_PP_SEQ_HEAD( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err_for_args))), \ + DBC_PP_FUN_PARSE_COPYABLE_ARG_ \ + , /* else */ \ + DBC_PP_FUN_PARSE_NONCOPYABLE_ARG_ \ + )(sign_traits_err_for_args) /* end if */ + +// Error Handling // + +#define DBC_PP_FUN_PARSE_ARGS_RETURN_(sign_traits_err, \ + sign_traits_err_for_args, max_args) \ + BOOST_PP_IF(BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_TUPLE_ELEM(3, 2, \ + sign_traits_err_for_args)), /* error */ \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_SEQ_TAIL( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)), \ + (/* empty seq elem on error */), \ + BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_TUPLE_ELEM(3, 2, \ + sign_traits_err_for_args))) \ + , BOOST_PP_IF(BOOST_PP_GREATER(BOOST_PP_SEQ_SIZE( \ + BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err_for_args)), max_args), \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_SEQ_TAIL( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)), \ + /* append args seq that contains too many elems */ \ + ( BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err_for_args) ), \ + (1, DBC_ERRPR_too_many_function_arguments)) \ + , /* else (no error) */ \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_SEQ_TAIL( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)), \ + ( BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err_for_args) ) ) \ + )) /* end if */ + +#define DBC_PP_FUN_PARSE_ARGS_OP_(sign_traits_err, max_args) \ + DBC_PP_FUN_PARSE_ARGS_RETURN_(sign_traits_err, \ + BOOST_PP_WHILE(DBC_PP_FUN_PARSE_ARGS_LOOP_PRED_, \ + DBC_PP_FUN_PARSE_ARGS_LOOP_OP_, /* init remaining arg sign */ \ + ( BOOST_PP_SEQ_HEAD(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))\ + , BOOST_PP_EMPTY() /* init parsed arg attrs seq */ \ + , (0, "") /* init no error */ ) \ + ) /* end while */ \ + , max_args) + +#define DBC_PP_FUN_PARSE_ARGS_NO_(sign_traits_err, max_args) \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + (/* empty seq elem in case of error */), \ + DBC_ERROR_missing_function_arguments_) + +// API // + +#define DBC_PP_FUN_PARSE_ARGS_(max_args, sign_traits_err) \ + DBC_PP_FUN_PARSE_APPLY_(DBC_PP_FUN_PARSE_ARGS_OP_, \ + DBC_PP_FUN_PARSE_ARGS_NO_, sign_traits_err, max_args) + +#endif // DBC_PP_FUN_PARSE_ARGS_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/base_type.hpp b/include/dbc/detail/pp/fun_parse/base_type.hpp new file mode 100755 index 00000000..9caaa49f --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/base_type.hpp @@ -0,0 +1,43 @@ + +#ifndef DBC_PP_FUN_PARSE_BASE_TYPE_HPP_ +#define DBC_PP_FUN_PARSE_BASE_TYPE_HPP_ + +#include "../keywords.hpp" +#include "util/apply.hpp" +#include "util/returns.hpp" +#include "util/required.hpp" +#include + +#define DBC_PP_FUN_PARSE_BASE_TYPE_NO_(sign_traits_err, data) \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + (/* empty seq elem if no token */)) + +#define DBC_PP_FUN_PARSE_BASE_TYPE_REQUIRED_(sign_traits_err, data) \ + BOOST_PP_IF(BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_TAIL( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))), \ + DBC_PP_FUN_PARSE_REQUIRED_OP_ \ + , /* else (no token left to parse in signature) */ \ + DBC_PP_FUN_PARSE_REQUIRED_NO_ \ + )( ( \ + BOOST_PP_SEQ_TAIL(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)) \ + , BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err) \ + , BOOST_PP_TUPLE_ELEM(3, 2, sign_traits_err) \ + ), \ + DBC_ERROR_missing_base_class_type_in_DBC_BASE_specifier_ \ + ) /* endif */ \ + +#define DBC_PP_FUN_PARSE_BASE_TYPE_OP_(sign_traits_err, data) \ + BOOST_PP_IF(DBC_PP_IS_BASE_(BOOST_PP_SEQ_HEAD( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))), \ + DBC_PP_FUN_PARSE_BASE_TYPE_REQUIRED_ \ + , /* else (no DBC_BASE() specifier found */ \ + DBC_PP_FUN_PARSE_BASE_TYPE_NO_ \ + )(sign_traits_err, data) /* endif */ + +#define DBC_PP_FUN_PARSE_BASE_TYPE_(sign_traits_err) \ + DBC_PP_FUN_PARSE_APPLY_(DBC_PP_FUN_PARSE_BASE_TYPE_OP_, \ + DBC_PP_FUN_PARSE_BASE_TYPE_NO_, sign_traits_err, ~) + +#endif // DBC_PP_FUN_PARSE_BASE_TYPE_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/class_type.hpp b/include/dbc/detail/pp/fun_parse/class_type.hpp new file mode 100755 index 00000000..65216082 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/class_type.hpp @@ -0,0 +1,26 @@ + +#ifndef DBC_PP_FUN_PARSE_CLASS_TYPE_HPP_ +#define DBC_PP_FUN_PARSE_CLASS_TYPE_HPP_ + +#include "../keywords.hpp" +#include "util/optional.hpp" +#include "util/copyable.hpp" +#include "util/match_not.hpp" + +#define DBC_PP_FUN_PARSE_CLASS_TYPE_(sign_traits_err) \ + DBC_PP_FUN_PARSE_COPYABLE_( \ + DBC_PP_FUN_PARSE_OPTIONAL_(sign_traits_err, DBC_PP_IS_TEMPLATE_), \ + DBC_ERROR_missing_class_type_ \ + ) + +#define DBC_PP_FUN_PARSE_UNCOPYABLE_CLASS_TYPE_(sign_traits_err) \ + DBC_PP_FUN_PARSE_COPYABLE_( \ + DBC_PP_FUN_PARSE_MATCH_NOT_(DBC_PP_IS_COPYABLE_, \ + DBC_ERROR_class_type_cannot_be_specified_copyable_in_this_context_, \ + DBC_PP_FUN_PARSE_OPTIONAL_(sign_traits_err, DBC_PP_IS_TEMPLATE_) \ + ), \ + DBC_ERROR_missing_class_type_ \ + ) + +#endif // DBC_PP_FUN_PARSE_CLASS_TYPE_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/const.hpp b/include/dbc/detail/pp/fun_parse/const.hpp new file mode 100755 index 00000000..6f03b6f8 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/const.hpp @@ -0,0 +1,12 @@ + +#ifndef DBC_PP_FUN_PARSE_CONST_HPP_ +#define DBC_PP_FUN_PARSE_CONST_HPP_ + +#include "../keywords.hpp" +#include "util/optional.hpp" + +#define DBC_PP_FUN_PARSE_CONST_(sign_traits_err) \ + DBC_PP_FUN_PARSE_OPTIONAL_(sign_traits_err, DBC_PP_IS_CONST_) + +#endif // DBC_PP_FUN_PARSE_CONST_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/name.hpp b/include/dbc/detail/pp/fun_parse/name.hpp new file mode 100755 index 00000000..d0fc3a35 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/name.hpp @@ -0,0 +1,11 @@ + +#ifndef DBC_PP_FUN_PARSE_NAME_HPP_ +#define DBC_PP_FUN_PARSE_NAME_HPP_ + +#include "util/required.hpp" + +#define DBC_PP_FUN_PARSE_NAME_(sign_traits_err) \ + DBC_PP_FUN_PARSE_REQUIRED_(sign_traits_err, DBC_ERROR_missing_function_name_) + +#endif // DBC_PP_FUN_PARSE_ARGS_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/result_type.hpp b/include/dbc/detail/pp/fun_parse/result_type.hpp new file mode 100755 index 00000000..0e9ff86e --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/result_type.hpp @@ -0,0 +1,11 @@ + +#ifndef DBC_PP_FUN_PARSE_RESULT_TYPE_HPP_ +#define DBC_PP_FUN_PARSE_RESULT_TYPE_HPP_ + +#include "util/required.hpp" + +#define DBC_PP_FUN_PARSE_RESULT_TYPE_(sign_traits_err) \ + DBC_PP_FUN_PARSE_REQUIRED_(sign_traits_err, DBC_ERROR_missing_result_type_) + +#endif // DBC_PP_FUN_PARSE_RESULT_TYPE_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/util/append_traits.hpp b/include/dbc/detail/pp/fun_parse/util/append_traits.hpp new file mode 100755 index 00000000..59f5d654 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/util/append_traits.hpp @@ -0,0 +1,20 @@ + +#ifndef DBC_PP_FUN_PARSE_APPEND_TRAITS_HPP_ +#define DBC_PP_FUN_PARSE_APPEND_TRAITS_HPP_ + +#include "apply.hpp" +#include "returns.hpp" +#include + +#define DBC_PP_FUN_PARSE_APPEND_TRAITS_OP_(sign_traits_err, traits_to_append) \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + traits_to_append) + +#define DBC_PP_FUN_PARSE_APPEND_TRAITS_(traits_to_append, sign_traits_err) \ + DBC_PP_FUN_PARSE_APPLY_(DBC_PP_FUN_PARSE_APPEND_TRAITS_OP_, \ + DBC_PP_FUN_PARSE_APPEND_TRAITS_OP_, sign_traits_err, \ + traits_to_append) + +#endif // DBC_PP_FUN_PARSE_APPEND_TRAITS_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/util/apply.hpp b/include/dbc/detail/pp/fun_parse/util/apply.hpp new file mode 100755 index 00000000..1331bf93 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/util/apply.hpp @@ -0,0 +1,24 @@ + +#ifndef DBC_PP_FUN_PARSE_APPLY_HPP_ +#define DBC_PP_FUN_PARSE_APPLY_HPP_ + +#include + +// Can't use BOOST_PP_IDENTITY() because it trails with BOOST_PP_EMPTY... +#define DBC_PP_FUN_PARSE_APPLY_IDENTITY_(sign_traits_err, data) sign_traits_err + +#define DBC_PP_FUN_PARSE_APPLY_(parse_macro, parse_empty_macro, \ + sign_traits_err, data) \ + BOOST_PP_IF(BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_TUPLE_ELEM( \ + 3, 2, sign_traits_err)), \ + /* if error, do nothing but passing error up */ \ + DBC_PP_FUN_PARSE_APPLY_IDENTITY_ \ + , BOOST_PP_IF(BOOST_PP_SEQ_SIZE( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)), \ + parse_macro \ + , /* else (no token left to parse in signature) */ \ + parse_empty_macro \ + ))(sign_traits_err, data) + +#endif // DBC_PP_FUN_PARSE_APPLY_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/util/copyable.hpp b/include/dbc/detail/pp/fun_parse/util/copyable.hpp new file mode 100755 index 00000000..e6113bec --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/util/copyable.hpp @@ -0,0 +1,27 @@ + +#ifndef DBC_PP_FUN_PARSE_COPYABLE_HPP_ +#define DBC_PP_FUN_PARSE_COPYABLE_HPP_ + +#include "../../keywords.hpp" +#include "../../seq.hpp" +#include "apply.hpp" +#include "required.hpp" +#include "optional.hpp" +#include + +#define DBC_PP_FUN_PARSE_COPYABLE_OP_(sign_traits_err, data) \ + ( BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err) \ + , DBC_PP_SEQ_REST_N_TO_TUPLE_(2, \ + BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err)) \ + , BOOST_PP_TUPLE_ELEM(3, 2, sign_traits_err)) + +#define DBC_PP_FUN_PARSE_COPYABLE_(sign_traits_err, missing_token_error) \ + DBC_PP_FUN_PARSE_APPLY_(DBC_PP_FUN_PARSE_COPYABLE_OP_, \ + DBC_PP_FUN_PARSE_COPYABLE_OP_, \ + DBC_PP_FUN_PARSE_REQUIRED_( \ + DBC_PP_FUN_PARSE_OPTIONAL_(sign_traits_err, \ + DBC_PP_IS_COPYABLE_), missing_token_error), \ + ~) + +#endif // DBC_PP_FUN_PARSE_COPYABLE_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/util/match_not.hpp b/include/dbc/detail/pp/fun_parse/util/match_not.hpp new file mode 100755 index 00000000..bfc79790 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/util/match_not.hpp @@ -0,0 +1,31 @@ + +#ifndef DBC_PP_FUN_PARSE_MATCH_NOT_HPP_ +#define DBC_PP_FUN_PARSE_MATCH_NOT_HPP_ + +#include "apply.hpp" +#include "returns.hpp" +#include + +#define DBC_PP_FUN_PARSE_MATCH_NOT_NO_(sign_traits_err, match_n_err) \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + /* append nothing -- this is just a check */) + +#define DBC_PP_FUN_PARSE_MATCH_NOT_OP_(sign_traits_err, match_n_err) \ + BOOST_PP_IF(BOOST_PP_TUPLE_ELEM(2, 0, match_n_err)/* macth macro */( \ + BOOST_PP_SEQ_HEAD(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))), \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + /* append nothing -- this is just a check */, \ + BOOST_PP_TUPLE_ELEM(2, 1, match_n_err) /* err msg */) \ + , /* else */ \ + DBC_PP_FUN_PARSE_MATCH_NOT_NO_(sign_traits_err, check_macro) \ + ) /* endif */ + +#define DBC_PP_FUN_PARSE_MATCH_NOT_(match_macro, match_err, sign_traits_err) \ + DBC_PP_FUN_PARSE_APPLY_(DBC_PP_FUN_PARSE_MATCH_NOT_OP_, \ + DBC_PP_FUN_PARSE_MATCH_NOT_NO_, sign_traits_err, \ + (match_macro, match_err)) + +#endif // DBC_PP_FUN_PARSE_MATCH_NOT_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/util/optional.hpp b/include/dbc/detail/pp/fun_parse/util/optional.hpp new file mode 100755 index 00000000..c9efabe0 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/util/optional.hpp @@ -0,0 +1,33 @@ + +#ifndef DBC_PP_FUN_PARSE_OPTIONAL_HPP_ +#define DBC_PP_FUN_PARSE_OPTIONAL_HPP_ + +#include "../../seq.hpp" +#include "apply.hpp" +#include "returns.hpp" +#include + +#define DBC_PP_FUN_PARSE_OPTIONAL_NO_(sign_traits_err, check_macro) \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + (/* empty seq elem if no token */)) + +#define DBC_PP_FUN_PARSE_OPTIONAL_OP_(sign_traits_err, check_macro) \ + BOOST_PP_IF(check_macro(BOOST_PP_SEQ_HEAD( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))), \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, BOOST_PP_SEQ_TAIL( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)), \ + (BOOST_PP_SEQ_HEAD(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)))\ + )\ + , /* else */ \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + () /* empty se elem if no macth */) \ + ) /* endif */ + +#define DBC_PP_FUN_PARSE_OPTIONAL_(sign_traits_err, check_macro) \ + DBC_PP_FUN_PARSE_APPLY_(DBC_PP_FUN_PARSE_OPTIONAL_OP_, \ + DBC_PP_FUN_PARSE_OPTIONAL_NO_, sign_traits_err, check_macro) + +#endif // DBC_PP_FUN_PARSE_OPTIONAL_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/util/required.hpp b/include/dbc/detail/pp/fun_parse/util/required.hpp new file mode 100755 index 00000000..dbf48c9e --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/util/required.hpp @@ -0,0 +1,38 @@ + +#ifndef DBC_PP_FUN_PARSE_REQUIRED_HPP_ +#define DBC_PP_FUN_PARSE_REQUIRED_HPP_ + +#include "../../seq.hpp" +#include "apply.hpp" +#include "returns.hpp" +#include +#include + +#define DBC_PP_FUN_PARSE_REQUIRED_NO_(sign_traits_err, missing_token_err) \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err), \ + (/* empty seq elem in case of error */), \ + missing_token_err) \ + +#define DBC_PP_FUN_PARSE_REQUIRED_OP_(sign_traits_err, \ + missing_token_err) \ + BOOST_PP_IF(BOOST_PP_IS_EMPTY(BOOST_PP_SEQ_HEAD( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err))), \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, BOOST_PP_SEQ_TAIL( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)), \ + BOOST_PP_EMPTY(), \ + missing_token_err) \ + , /* else */ \ + DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, BOOST_PP_SEQ_TAIL( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)), \ + ( BOOST_PP_SEQ_HEAD( \ + BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)) )) \ + ) /* endif */ + +#define DBC_PP_FUN_PARSE_REQUIRED_(sign_traits_err, missing_token_err) \ + DBC_PP_FUN_PARSE_APPLY_(DBC_PP_FUN_PARSE_REQUIRED_OP_, \ + DBC_PP_FUN_PARSE_REQUIRED_NO_, sign_traits_err, \ + missing_token_err) + +#endif // DBC_PP_FUN_PARSE_REQUIRED_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/util/returns.hpp b/include/dbc/detail/pp/fun_parse/util/returns.hpp new file mode 100755 index 00000000..6d11b937 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/util/returns.hpp @@ -0,0 +1,30 @@ + +#ifndef DBC_PP_FUN_PARSE_RETURNS_HPP_ +#define DBC_PP_FUN_PARSE_RETURNS_HPP_ + +#include + +#define DBC_PP_FUN_PARSE_RETURN_OK_(sign_traits_err, \ + remaining_sign, append_traits) \ + ( /* return new sing_traits_err 3-tuple */ \ + /* signature still to be parsed */ \ + remaining_sign, \ + /* append specified new attrs to existing ones (seq concatenation) */ \ + BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err) append_traits, \ + /* no error (code, message) */ \ + (0, "") \ + ) + +#define DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + remaining_sign, append_traits, error_msg) \ + ( /* return new sing_traits_err 3-tuple */ \ + /* signature still to be parsed */ \ + remaining_sign, \ + /* append specified new attrs to existing ones (seq concatenation) */ \ + BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err) append_traits, \ + /* error (code, message) */ \ + (1, error_msg) \ + ) + +#endif // DBC_PP_FUN_PARSE_RETURNS_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/util/validate.hpp b/include/dbc/detail/pp/fun_parse/util/validate.hpp new file mode 100755 index 00000000..e7e8e42c --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/util/validate.hpp @@ -0,0 +1,31 @@ + +#ifndef DBC_PP_FUN_PARSE_VALIDATE_HPP_ +#define DBC_PP_FUN_PARSE_VALIDATE_HPP_ + +#include "returns.hpp" +#include + +#define DBC_PP_FUN_PARSE_VALIDATE_(valid_traits_size, sign_traits_err) /**/ \ + BOOST_PP_IF(BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_TUPLE_ELEM( \ + 3, 2, sign_traits_err)), \ + sign_traits_err /* already an error, just return it */ \ + , BOOST_PP_IF(BOOST_PP_SEQ_SIZE(BOOST_PP_TUPLE_ELEM( \ + 3, 0, sign_traits_err)), \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_EMPTY(), /* empty signature */ \ + BOOST_PP_EMPTY(), /* don't append any traits */ \ + DBC_ERROR_extra_tokens_in_function_signature_ /* remaining tokens are BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(3, 0, sign_traits_err)) */) \ + , BOOST_PP_IF(BOOST_PP_NOT_EQUAL(valid_traits_size, BOOST_PP_SEQ_SIZE( \ + BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err))), \ + DBC_PP_FUN_PARSE_RETURN_ERR_(sign_traits_err, \ + BOOST_PP_EMPTY(), /* empty signature */ \ + BOOST_PP_EMPTY(), /* don't append any traits */ \ + /* maybe this only happens on internal error... I'm not sure */ \ + DBC_ERROR_wrong_number_of_traits_after_parsing_function_signature_ /* BOOST_PP_STRINGIZE(valid_traits_size) when BOOST_PP_STRINGIZE(BOOST_PP_SEQ_SIZE(BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err))) found */) \ + , /* else (it's valid, no error) */ \ + sign_traits_err \ + ))) \ + /**/ + +#endif // DBC_PP_FUN_PARSE_VALIDATE_HPP_ + diff --git a/include/dbc/detail/pp/fun_parse/virtual.hpp b/include/dbc/detail/pp/fun_parse/virtual.hpp new file mode 100755 index 00000000..37789237 --- /dev/null +++ b/include/dbc/detail/pp/fun_parse/virtual.hpp @@ -0,0 +1,12 @@ + +#ifndef DBC_PP_FUN_PARSE_VIRTUAL_HPP_ +#define DBC_PP_FUN_PARSE_VIRTUAL_HPP_ + +#include "../keywords.hpp" +#include "util/optional.hpp" + +#define DBC_PP_FUN_PARSE_VIRTUAL_(sign_traits_err) \ + DBC_PP_FUN_PARSE_OPTIONAL_(sign_traits_err, DBC_PP_IS_VIRTUAL_) + +#endif // DBC_PP_FUN_PARSE_VIRTUAL_HPP_ + diff --git a/include/dbc/detail/pp/fun_traits.hpp b/include/dbc/detail/pp/fun_traits.hpp new file mode 100755 index 00000000..452fe940 --- /dev/null +++ b/include/dbc/detail/pp/fun_traits.hpp @@ -0,0 +1,82 @@ + +#ifndef DBC_PP_FUN_TRAITS_HPP_ +#define DBC_PP_FUN_TRAITS_HPP_ + +#include "keywords.hpp" +#include + +/** + * Inspect specified function traits as parsed from preprocessor function + * signature. + * In this context function can be either a member or non member function. + * For non member function some traits will trivially return BOOST_PP_EMPTY() + * (like the trait to get the function access DBC_PP_MEM_GET_ACCESS_()). + * The "f" parameter below is the function traits sequence as the 2nd element + * of the tuple returned by the DBC_PP_FUN_() macro. + */ + +#define DBC_PP_FUN_TRAITS_COUNT_ 9 // Index in [0, COUNT - 1]. + +#define DBC_PP_FUN_GET_ACCESS_(f) BOOST_PP_SEQ_ELEM(0, f) +#define DBC_PP_FUN_IS_PRIVATE_(f) DBC_PP_IS_PRIVATE_(DBC_PP_FUN_GET_ACCESS_(f)) +#define DBC_PP_FUN_IS_PROTECTED_(f) \ + DBC_PP_IS_PROTECTED_(DBC_PP_FUN_GET_ACCESS_(f)) +#define DBC_PP_FUN_IS_PUBLIC_(f) DBC_PP_IS_PUBLIC_(DBC_PP_FUN_GET_ACCESS_(f)) + +#define DBC_PP_FUN_GET_VIRTUAL_(f) BOOST_PP_SEQ_ELEM(1, f) +#define DBC_PP_FUN_IS_VIRTUAL_(f) \ + BOOST_PP_NOT(BOOST_PP_IS_EMPTY(DBC_PP_FUN_GET_VIRTUAL_(f))) + +#define DBC_PP_FUN_GET_RESULT_TYPE_(f) BOOST_PP_SEQ_ELEM(2, f) +#define DBC_PP_FUN_IS_VOID_(f) DBC_PP_IS_VOID_(DBC_PP_FUN_GET_RESULT_TYPE_(f)) +#define DBC_PP_FUN_GET_RETURN_(f) \ + BOOST_PP_IF(DBC_PP_FUN_IS_VOID_(f), BOOST_PP_EMPTY(), return) + +#define DBC_PP_FUN_GET_TEMPLATE_(f) BOOST_PP_SEQ_ELEM(3, f) +#define DBC_PP_FUN_IS_TEMPLATE_(f) \ + BOOST_PP_NOT(BOOST_PP_IS_EMPTY(DBC_PP_FUN_GET_TEMPLATE_(f))) +#define DBC_PP_FUN_GET_TYPENAME_(f) \ + BOOST_PP_IF(DBC_PP_FUN_IS_TEMPLATE_(f), typename, BOOST_PP_EMPTY()) + +#define DBC_PP_FUN_IS_CLASS_COPYABLE_(f) \ + BOOST_PP_NOT(BOOST_PP_IS_EMPTY( \ + BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_SEQ_ELEM(4, f)))) +#define DBC_PP_FUN_GET_CLASS_TYPE_(f) \ + BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_SEQ_ELEM(4, f)) +#define DBC_PP_FUN_GET_CLASS_COPYABLE_TYPE_(f) \ + BOOST_PP_EXPR_IF(DBC_PP_FUN_IS_CLASS_COPYABLE_(f), ::dbc::copyable<) \ + DBC_PP_FUN_GET_CLASS_TYPE_(f) \ + BOOST_PP_EXPR_IF(DBC_PP_FUN_IS_CLASS_COPYABLE_(f), >) + +#define DBC_PP_FUN_GET_BASE_TYPE_(f) BOOST_PP_SEQ_ELEM(5, f) +#define DBC_PP_FUN_IS_DERIVED_(f) \ + BOOST_PP_NOT(BOOST_PP_IS_EMPTY(DBC_PP_FUN_GET_BASE_TYPE_(f))) + +#define DBC_PP_FUN_GET_NAME_(f) BOOST_PP_SEQ_ELEM(6, f) + +#define DBC_PP_FUN_GET_ARGS_(f) BOOST_PP_SEQ_ELEM(7, f) +#define DBC_PP_FUN_HAS_ARGS_(f) BOOST_PP_SEQ_SIZE(DBC_PP_FUN_GET_ARGS_(f)) +#define DBC_PP_FUN_GET_ARG_(n, f) BOOST_PP_SEQ_ELEM(n, DBC_PP_FUN_GET_ARGS_(f)) + +#define DBC_PP_FUN_IS_ARG_COPYABLE_(arg) \ + BOOST_PP_NOT(BOOST_PP_IS_EMPTY(BOOST_PP_TUPLE_ELEM(3, 0, arg))) +#define DBC_PP_FUN_GET_ARG_TYPE_(arg) BOOST_PP_TUPLE_ELEM(3, 1, arg) +#define DBC_PP_FUN_GET_ARG_COPYABLE_TYPE_(arg) \ + BOOST_PP_EXPR_IF(DBC_PP_FUN_IS_ARG_COPYABLE_(arg), ::dbc::copyable<) \ + DBC_PP_FUN_GET_ARG_TYPE_(arg) \ + BOOST_PP_EXPR_IF(DBC_PP_FUN_IS_ARG_COPYABLE_(arg), >) +#define DBC_PP_FUN_GET_ARG_NAME_(arg) BOOST_PP_TUPLE_ELEM(3, 2, arg) + +#define DBC_PP_FUN_GET_CONST_(f) BOOST_PP_SEQ_ELEM(8, f) +#define DBC_PP_FUN_IS_CONST_(f) \ + BOOST_PP_NOT(BOOST_PP_IS_EMPTY(DBC_PP_FUN_GET_CONST_(f))) + +#define DBC_PP_FUN_GET_CONST_CLASS_COPYABLE_TYPE_(f) \ + /* const qualifier must be within copyable<> */ \ + BOOST_PP_EXPR_IF(DBC_PP_FUN_IS_CLASS_COPYABLE_(f), ::dbc::copyable<) \ + DBC_PP_FUN_GET_CONST_(f) \ + DBC_PP_FUN_GET_CLASS_TYPE_(f) \ + BOOST_PP_EXPR_IF(DBC_PP_FUN_IS_CLASS_COPYABLE_(f), >) + +#endif // DBC_PP_FUN_TRAITS_HPP_ + diff --git a/include/dbc/detail/pp/keywords.hpp b/include/dbc/detail/pp/keywords.hpp new file mode 100755 index 00000000..77912dad --- /dev/null +++ b/include/dbc/detail/pp/keywords.hpp @@ -0,0 +1,53 @@ + +#ifndef DBC_PP_FUN_KEYWORDS_HPP_ +#define DBC_PP_FUN_KEYWORDS_HPP_ + +#include +#include + +// Do NOT #undef these macros! They are mixed capital because their postfix +// must be the keyword to match but they are NOT local macros. +#define DBC_PP_PRIVATE_private () +#define DBC_PP_PROTECTED_protected () +#define DBC_PP_PUBLIC_public () +#define DBC_PP_VIRTUAL_virtual () +#define DBC_PP_VOID_void () +#define DBC_PP_TEMPLATE_template () +#define DBC_PP_CONST_const () +// Postfix must match symbol prefixed by DBC_BASE() macro. +#define DBC_PP_BASE_dbc_base_ () +// Postfix must match symbol prefixed by DBC_COPYABLE() macro. +#define DBC_PP_COPYABLE_dbc_copyable_ () + +#define DBC_PP_IS_FUN_KEYWORD_(keyword_prefix, token) \ + BOOST_PP_IS_UNARY(BOOST_PP_CAT(keyword_prefix, token)) + +#define DBC_PP_IS_PRIVATE_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_PRIVATE_, token) + +#define DBC_PP_IS_PROTECTED_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_PROTECTED_, token) + +#define DBC_PP_IS_PUBLIC_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_PUBLIC_, token) + +#define DBC_PP_IS_VIRTUAL_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_VIRTUAL_, token) + +#define DBC_PP_IS_VOID_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_VOID_, token) + +#define DBC_PP_IS_TEMPLATE_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_TEMPLATE_, token) + +#define DBC_PP_IS_CONST_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_CONST_, token) + +#define DBC_PP_IS_BASE_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_BASE_, token) + +#define DBC_PP_IS_COPYABLE_(token) \ + DBC_PP_IS_FUN_KEYWORD_(DBC_PP_COPYABLE_, token) + +#endif // DBC_PP_FUN_KEYWORDS_HPP_ + diff --git a/include/dbc/detail/pp/seq.hpp b/include/dbc/detail/pp/seq.hpp new file mode 100755 index 00000000..f5e9bcfe --- /dev/null +++ b/include/dbc/detail/pp/seq.hpp @@ -0,0 +1,15 @@ + +#ifndef DBC_PP_SEQ_HPP_ +#define DBC_PP_SEQ_HPP_ + +#include + +/** Conver last n elem of specified seq into a tuple. */ +#define DBC_PP_SEQ_REST_N_TO_TUPLE_(n, seq) \ + BOOST_PP_SEQ_FIRST_N(BOOST_PP_SUB(BOOST_PP_SEQ_SIZE(seq), n), seq) \ + /* seq (t0)...(tk-2) concatenated w/ (tk-1, tk) for (copyalbe, type) */ \ + ( BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_REST_N(BOOST_PP_SUB( \ + BOOST_PP_SEQ_SIZE(seq), n), seq)) ) + +#endif // DBC_PP_SEQ_HPP_ + diff --git a/include/dbc/detail/threading/sync.hpp b/include/dbc/detail/threading/sync.hpp new file mode 100755 index 00000000..9725d7a3 --- /dev/null +++ b/include/dbc/detail/threading/sync.hpp @@ -0,0 +1,26 @@ + +#ifndef DBC_SYNC_HPP_ +#define DBC_SYNC_HPP_ + +namespace dbc { + +template +class sync_ { +public: + sync_(const T& value = DEFAULT): value_(value) {} + + /** @todo[LC] If DBC_THREADING, sync this operation with a mutex declared + * mutable in this class' state. */ + + const T& operator=(const T& value) { return value_ = value; } + + operator T() const { return value_; } + +private: + T value_; +}; + +} // namespace dbc + +#endif // DBC_SYNC_HPP_ + diff --git a/include/dbc/detail/type_traits.hpp b/include/dbc/detail/type_traits.hpp new file mode 100755 index 00000000..ee9245da --- /dev/null +++ b/include/dbc/detail/type_traits.hpp @@ -0,0 +1,40 @@ + +#ifndef DBC_TYPE_TRAITS_HPP_ +#define DBC_TYPE_TRAITS_HPP_ + +#include "../copyable.hpp" + +#define DBC_ADD_CONST_REF_(typename_keyword, the_type) \ + const typename_keyword ::dbc::remove_const_ref_< the_type >::type& + +// Implement metafunctions similar (but much simpler) to Boost.TypeTraits to +// work-around some Boost compiler specific (gcc) bugs (bugs: #23771, #23691). + +namespace dbc { + +// Remove const& // + +/** Primary template, remove nothing. */ +template struct remove_const_ref_ { typedef T type; }; + +/** Specialization to remove only const. */ +template struct remove_const_ref_ { typedef T type; }; + +/** Specialization to remove only reference. */ +template struct remove_const_ref_ { typedef T type; }; + +/** Specialization to remove both reference and const. */ +template struct remove_const_ref_ { typedef T type; }; + +// Copyable // + +/** Primary template, remove nothing. */ +template struct remove_copyable_ { typedef T type; }; + +/** Specialization to remove copyable. */ +template struct remove_copyable_ > { typedef T type; }; + +} // namespace dbc + +#endif // DBC_TYPE_TRAITS_HPP_ + diff --git a/include/dbc/exception.hpp b/include/dbc/exception.hpp new file mode 100755 index 00000000..4b39842d --- /dev/null +++ b/include/dbc/exception.hpp @@ -0,0 +1,98 @@ + +#ifndef DBC_EXCEPTION_HPP_ +#define DBC_EXCEPTION_HPP_ + +#include "config.hpp" +#include +#include +#include + +#ifdef DBC + +#define DBC_RETHROW_VIOLATION_AS_(rethrow_violation_type, code) { \ + try { code; } \ + catch (::dbc::condition_violation& ex) \ + { throw ::dbc::rethrow_violation_type(ex.what()); } \ + /* leave other exceptions uncathed */ \ +} + +#endif // DBC + +// Following must be present regardless of DBC #definition. + +namespace dbc { + +// Exceptions // + +class condition_violation: public std::logic_error { +public: + explicit condition_violation(const std::string& what): + std::logic_error(what) {} +}; + +class precondition_violation: public condition_violation { +public: + explicit precondition_violation(const std::string& what): + condition_violation(what) {} +}; + +class postcondition_violation: public condition_violation { +public: + explicit postcondition_violation(const std::string& what): + condition_violation(what) {} +}; + +class invariant_violation: public condition_violation { +public: + explicit invariant_violation(const std::string& what): + condition_violation(what) {} +}; + +class variant_violation: public condition_violation { +public: + explicit variant_violation(const std::string& what): + condition_violation(what) {} +}; + +// Broken Destructor Handler // + +// This follows std::terminate() handling mechanism and conventions (also, it's +// defined here as std::terminate() handling is defined in ). + +typedef void (*broken_destructor_invariant_handler)(); + +static broken_destructor_invariant_handler + broken_destructor_invariant_handler_ = 0; + +inline broken_destructor_invariant_handler set_broken_destructor_invariant( + broken_destructor_invariant_handler handler) { + return (broken_destructor_invariant_handler_ = handler); +} + +inline void broken_destructor_invariant() { + if (broken_destructor_invariant_handler_) { + broken_destructor_invariant_handler_(); + } else { +#if defined DBC_CONFIG_ENABLE_TERMINATING_ASSERTIONS + std::cerr << "Terminating because destrcutor thrown unhandeled " + << "invariant violation exception" << std::endl; + std::terminate(); +#elif defined DBC_CONFIG_ENABLE_EXITING_ASSERTIONS + std::cerr << "Exiting with code 1 because destrcutor thrown " + << "unhandeled invariant violation exception" << std::endl; + ::exit(1); +#else + // If user does not want assertion to terminate or exit, we just log + // an error. However, this is very dangerous as the program is now in + // a bad state and we are continuing its execution... + std::clog << "Ignoring unhandeled invariant violation exception " + << "thrown in destructor -- program might be in a bad state" + << std::endl; +#endif + } +} + +} // namespace dbc + +#endif // DBC_EXCEPTION_HPP_ + diff --git a/include/dbc/fun.hpp b/include/dbc/fun.hpp new file mode 100755 index 00000000..27323d2f --- /dev/null +++ b/include/dbc/fun.hpp @@ -0,0 +1,669 @@ + +#ifdef DBC + +#include "config.hpp" +#include "post.hpp" +#include "exception.hpp" +#include "object.hpp" +#include "detail/type_traits.hpp" +#include "detail/none.hpp" +#include "detail/checking.hpp" +#include "detail/logging.hpp" +#include +#include +#include +#include +#include + +// No Self-Iteration // + +#if !BOOST_PP_IS_ITERATING + // Include guards but ONLY when not self-iterating (and NOT below). +# ifndef DBC_FUN_HPP_ +# define DBC_FUN_HPP_ + // Iterate over function-arity (i.e., argument number). +# define BOOST_PP_ITERATION_PARAMS_1 (3, (0, DBC_CONFIG_MAX_ARGC, \ + DBC_CONFIG_FUN_HPP_FILE_PATH)) +# include BOOST_PP_ITERATE() // Iterate. +# endif // DBC_FUN_HPP_ + +// 1st Self-Iteration (over function argument count) // + +#elif BOOST_PP_ITERATION_DEPTH() == 1 + // Iterate over 0 void, 1 non-void function. +# define BOOST_PP_ITERATION_PARAMS_2 (3, (0, 1, \ + DBC_CONFIG_FUN_HPP_FILE_PATH)) +# include BOOST_PP_ITERATE() // Iterate. + +// 2nd Self-Iteration (over void/non-void Functions) // + +#elif BOOST_PP_ITERATION_DEPTH() == 2 + // Remaining argument number = max arg count - current arg number. +# define DBC_rest_arg_num BOOST_PP_FRAME_ITERATION(1) + // Argument number for fun template of this iteration. +# define DBC_arg_num BOOST_PP_SUB(BOOST_PP_FRAME_FINISH(1), \ + DBC_rest_arg_num) + // Wheter fun template of this iteration is for void function. +# define DBC_is_void BOOST_PP_FRAME_ITERATION(2) // Is void fun. +# define DBC_is_not_void BOOST_PP_NOT(DBC_is_void) + // It's templ specialization if void or arg num less than max arg cnt. +# define DBC_is_spec BOOST_PP_OR(DBC_is_void, DBC_rest_arg_num) +# define DBC_is_not_spec BOOST_PP_NOT(DBC_is_spec) + // Arguments. +# define DBC_arg_name(z, n, data) BOOST_PP_CAT(arg, n) +# define DBC_arg_type(z, n, data) \ + BOOST_PP_CAT(DBC_arg_name(z, n, data), _type) +# define DBC_arg_copyable_type(z, n, data) \ + BOOST_PP_CAT(DBC_arg_name(z, n, data), _copyable_type) +# define DBC_arg(z, n, data) DBC_arg_type(z, n, data) \ + DBC_arg_name(z, n, data) +# define DBC_arg_const_ref(z, n, data) DBC_ADD_CONST_REF_(typename, \ + DBC_arg_type(z, n, data)) DBC_arg_name(z, n, data) +# define DBC_post_arg_const_ref(z, n, data) \ + DBC_ADD_POST_CONST_REF_(typename, \ + DBC_arg_copyable_type(z, n, data)) DBC_arg_name(z, n, data) + // Post local variable. +# define DBC_post_var_name(z, n, data) \ + BOOST_PP_CAT(post_, DBC_arg_name(z, n, data)) +# define DBC_post_var_decl(z, n, data) \ + DBC_ADD_POST_(typename, DBC_arg_copyable_type(z, n, data)) \ + DBC_post_var_name(z, n, data)(DBC_arg_name(z, n, data)); + +// Here "xzy_copyable_type" is for a type that keep the dbc::copyable<> +// attribute if had one, while "xzy_type" will have the dbc::copyable<> +// removed. Therefore, "xzy_type" is guarranteed to not be copyable, while +// "xzy_copyable_type" will be copyable only if the type was specified as +// dbc::copyable<>. + +namespace dbc { + +template< + BOOST_PP_EXPR_IF(DBC_is_not_void, typename R) + BOOST_PP_COMMA_IF(BOOST_PP_AND(DBC_is_not_void, DBC_arg_num)) +# define DBC_tparam_arg(z, n, data) BOOST_PP_CAT(typename A, n) \ + BOOST_PP_EXPR_IF(DBC_is_not_spec, = none_) + // A0 = none_, A1 = none_, ... (if spec, no " = none_"). + BOOST_PP_ENUM(DBC_arg_num, DBC_tparam_arg, ~) +# undef DBC_tparam_arg + > +class fun + // Need separate lines because macro can't pass "< , >" tokens. + BOOST_PP_EXPR_IF(DBC_is_spec, <) + // void or R + BOOST_PP_IF(DBC_is_void, void, BOOST_PP_EXPR_IF(DBC_rest_arg_num, R)) + BOOST_PP_COMMA_IF(BOOST_PP_AND(DBC_is_spec, DBC_arg_num)) + // A0, A1, ... + BOOST_PP_IF(DBC_is_spec, BOOST_PP_ENUM_PARAMS, BOOST_PP_TUPLE_EAT(2)) + (DBC_arg_num, A) +# define DBC_none(z, n, data) none_ + // none_, none_, ... + BOOST_PP_ENUM_TRAILING(DBC_rest_arg_num, DBC_none, ~) +# undef DBC_none + BOOST_PP_EXPR_IF(DBC_is_spec, >) { +public: + typedef BOOST_PP_IF(DBC_is_void, void, R) result_type; + + // Generate "typedef An argn_copyable_type;". + // Use An instead of argn_type when copyable qualification needed. +# define DBC_arg_copyable_typedef(z, n, data) \ + typedef BOOST_PP_CAT(A, n) DBC_arg_copyable_type(z, n, data); + BOOST_PP_REPEAT(DBC_arg_num, DBC_arg_copyable_typedef, ~) +# undef DBC_arg_copyable_typedef + + // Generate "typedef typename remove_copyable argn_type;". + // Use An instead of argn_type when copyable qualification needed. +# define DBC_arg_typedef(z, n, data) \ + typedef typename ::dbc::remove_copyable_< \ + BOOST_PP_CAT(A, n) >::type DBC_arg_type(z, n, data); + BOOST_PP_REPEAT(DBC_arg_num, DBC_arg_typedef, ~) +# undef DBC_arg_typedef + +/** @todo[LC] Fix this "statically assert no Ai is void", it gives me compilation errors... also is should use argi_type (without copyable<> qualifier) and not Ai. */ +//# define DBC_assert_non_void_arg(z, n, data) +// && boost::mpl::not_ >::value +// BOOST_MPL_ASSERT_MSG((1 /* start w/ 1 so all repeats can use && prefix */ +// BOOST_PP_REPEAT(DBC_arg_num, DBC_assert_non_void_arg, ~)), +// DBC_ERROR_function_argument_type_An_cannot_be_void_, ()); +//# undef DBC_assert_non_void_arg + +private: + /** Dummy implementation never executed at run-time (but must compile). */ + template + class no_base { + public: + typedef C copyable_class_type; + typedef typename dbc::remove_copyable_::type class_type; + + void fun_name(const std::string& name) {} + + void check_invariant( + DBC_ADD_CONST_REF_(typename, class_type) self) {} + + void check_require( + DBC_ADD_CONST_REF_(typename, class_type) self + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg_const_ref, ~) + ) {} + + void check_ensure( + DBC_ADD_POST_CONST_REF_(typename, copyable_class_type) self + BOOST_PP_ENUM_TRAILING(DBC_arg_num, + DBC_post_arg_const_ref, ~) + BOOST_PP_COMMA_IF(DBC_is_not_void) + BOOST_PP_EXPR_IF(DBC_is_not_void, + DBC_ADD_CONST_REF_(typename, result_type) result) + ) {} + }; // class no_base + + // Following code heavely uses #ifdef DBC_CHECKING... to complie only the + // code that must be there based on #defined symbol combinations. This is + // done for optimization (1) reducing object code size and (2) removing + // executiong of code that is not needed -- but it complicates this code + // structure a bit. + +public: + template > + class mem { + public: + BOOST_MPL_ASSERT_MSG(boost::is_class::value, + DBC_ERROR_class_type_C_must_be_a_class_, ()); + BOOST_MPL_ASSERT_MSG(boost::is_class::value, + DBC_ERROR_base_contract_type_B_must_be_a_class_, ()); + // Static asserting "C inherits from dbc::object" is done by + // DBC_INVARIANT() so to generate compiler error in a signle spot if + // assertion is false. + /** @todo[LC] Statically assert: + * (3) if B != no_base then C is derived from B::class_type + * (4) there exist a friend non-mem fun dbc_invariant_() for C -- can + * I statically assert this? + * (5) C is not a template (incomple type)... this is the error I get + * if I forget to declare (template) the class_type... but I can I + * statically assert this? + */ +// BOOST_MPL_ASSERT_MSG((boost::mpl::if_, B>, +// boost::is_base_of, +// boost::mpl::true_type)::value, +// DBC_ERROR_class_type_C_must_be_derived_from_base_B_class_type_, +// ()); + + typedef mem this_type; + typedef C copyable_class_type; + typedef typename dbc::remove_copyable_::type class_type; + typedef B base_contract_type; + + typedef result_type (class_type::* mem_fun_type) + // ( arg0_type, arg1_type, ... ); + ( BOOST_PP_ENUM(DBC_arg_num, DBC_arg_type, ~) ); + + /** @todo[LC] Is there a better way to get the type-name using MPL? */ + explicit mem(): base_contract_(), + class_name_(typeid(class_type).name()), fun_name_(), + required_(false) { +#if DBC_CHECK_REQUIRE || DBC_CHECK_ENSURE || DBC_CHECK_INVAIRANT + // For optimization, don't construct base if not checking. + if (!boost::is_same< + base_contract_type, no_base >::value) { + base_contract_ = new base_contract_type(); + } +#endif // check + } + + virtual ~mem() { if (base_contract_) { delete base_contract_; } } + + inline void fun_name(const std::string& name) { + fun_name_ = name; + if (base_contract_) { base_contract_->fun_name(name); } + } + + inline virtual result_type operator()( + // Only add ref here (it's const only if it was already const). + typename boost::add_reference::type self + , mem_fun_type body + // , arg0_type arg0, arg1_type arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg, ~) + , const std::string& the_fun_name = "" + ) { + return call(self, body + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg_name, ~) + , the_fun_name); + } + +#if DBC_CHECK_INVARIANT + inline virtual void check_invariant( + DBC_ADD_CONST_REF_(typename, class_type) self) { + try { + ::dbc::globally_checking_contract_ = true; + DBC_LOG_DEBUG_(log << name() << "checking invariant"); + DBC_RETHROW_VIOLATION_AS_(invariant_violation, { + dbc_invariant_(self); + }); + // And, base contract invariant (subcontracting). + if (base_contract_) { base_contract_->check_invariant(self); } + ::dbc::globally_checking_contract_ = false; + } catch (...) { + ::dbc::globally_checking_contract_ = false; + throw; + } + } +#endif // check + +#if DBC_CHECK_REQUIRE + inline virtual void check_require( + DBC_ADD_CONST_REF_(typename, class_type) self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg_const_ref, ~) + ) { + try { + ::dbc::globally_checking_contract_ = true; + DBC_LOG_DEBUG_(log << name() << "checking preconditions"); + required_ = false; + try { + DBC_RETHROW_VIOLATION_AS_(precondition_violation, { + require(self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, + DBC_arg_name, ~)); + }); + // Iff my required and only mine passed (no invariant + // and/or base contract's require checking) -- user can use + // this in subcontract guards. + required_ = true; + } catch (...) { // Or, base contract preconditions (subcontr). + if (base_contract_) { + base_contract_->check_require(self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, + DBC_arg_name, ~) + ); + } else { throw; } + } // try require or base contract require. + ::dbc::globally_checking_contract_ = false; + } catch (...) { + ::dbc::globally_checking_contract_ = false; + throw; + } + } +#endif // check + +#if DBC_CHECK_ENSURE + inline virtual void check_ensure( + DBC_ADD_POST_CONST_REF_(typename, copyable_class_type) self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, + DBC_post_arg_const_ref, ~) + BOOST_PP_COMMA_IF(DBC_is_not_void) + BOOST_PP_EXPR_IF(DBC_is_not_void, + DBC_ADD_CONST_REF_(typename, result_type) result) + ) { + try { + ::dbc::globally_checking_contract_ = true; + DBC_LOG_DEBUG_(log << name() << "checking postconditions"); + DBC_RETHROW_VIOLATION_AS_(postcondition_violation, { + ensure(self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, + DBC_arg_name, ~) + BOOST_PP_COMMA_IF(DBC_is_not_void) + BOOST_PP_EXPR_IF(DBC_is_not_void, result)); + }); + // And, base contract postconditions (subcontracting). + if (base_contract_) { + DBC_ADD_POST_(typename, typename + base_contract_type::copyable_class_type) + base_self(self); + base_contract_->check_ensure(base_self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, + DBC_arg_name, ~) + BOOST_PP_COMMA_IF(DBC_is_not_void) + BOOST_PP_EXPR_IF(DBC_is_not_void,result) + ); + } + ::dbc::globally_checking_contract_ = false; + } catch (...) { + ::dbc::globally_checking_contract_ = false; + throw; + } + } +#endif // check + + protected: +#if DBC_CHECK_REQUIRE + virtual void require( + DBC_ADD_CONST_REF_(typename, class_type) self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg_const_ref, ~) + ) = 0; +#endif // check + +#if DBC_CHECK_ENSURE + virtual void ensure( + DBC_ADD_POST_CONST_REF_(typename, copyable_class_type) self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, + DBC_post_arg_const_ref, ~) + BOOST_PP_COMMA_IF(DBC_is_not_void) + BOOST_PP_EXPR_IF(DBC_is_not_void, + DBC_ADD_CONST_REF_(typename, result_type) result) + ) = 0; +#endif // check + + /** + * If this contract preconditions passed (for subcontracting guards). + * If the preconditions of this contract passed -- regardless of + * whether its invariant, its base contract preconditions, and its base + * contract invariant passed or not. This is set correctly only when + * this contract ensure() is called while it is always false within + * this contract require(). This can be used to write postconditions + * guards in this contract ensure() that allow for more flexible + * subcontracting -- see Design By Contract, by Examples. + * @return Whether this contract preconditions (and only those) passed. + */ + inline bool required() const { return required_; } + + inline result_type call( + // Only add ref here (it's const only if it was already const). + typename boost::add_reference::type self + , mem_fun_type body + // , arg0_type arg0, arg1_type arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg, ~) + , const std::string& the_fun_name = "" + , const bool& pre_invariant_check = true + , const bool& require_check = true + , const bool& post_invariant_check = true + , const bool& ensure_check = true + ) { +#if !DBC_CHECK_REQUIRE && !DBC_CHECK_ENSURE && !DBC_CHECK_INVARIANT + // For optimization, just call body if not checking. + return (self.*body)( + // arg0, arg1, ... + BOOST_PP_ENUM(DBC_arg_num, DBC_arg_name, ~) + ); + +#else // check + fun_name(the_fun_name); + + // Assertions disabled within assertions. + if (globally_checking_contract_) { + DBC_LOG_DEBUG_(log << name() << "entering, executing " + << "body, and returning (assertions disabled within " + << "assertion checking)" + ); + return (self.*body)( + // arg0, arg1, ... + BOOST_PP_ENUM(DBC_arg_num, DBC_arg_name, ~) + ); + } + DBC_LOG_DEBUG_(log << name() << "entering"); + + bool invariant_check = false; +#if DBC_CHECK_INVARIANT + if (pre_invariant_check || post_invariant_check) { + // Invariant checking disabled in class' nested calls (in + // nested calls, obj will be checing contract). + // Casting resolves ambiguities with base class' dbc::object<>. + invariant_check = !( + ((const dbc::object&)self) + .dbc_object_checking_contract_); + if (!invariant_check) { + DBC_LOG_DEBUG_(log << name() + << "invariant checking disabled (nested call)"); + } + } +#endif // check + // Casting resolves ambiguities with base class' dbc::object<>. + ((const dbc::object&)self) + .dbc_object_checking_contract_ = true; + + try { +#if DBC_CHECK_ENSURE + // Ideally, the post args will be declared only if + // (ensure_check) but then they will be local to the if code + // complicating this code structure quite a bit. For now, the + // only time ensure_check == false is for destrcustors which + // have no arguments and a noncopyable self so these post + // declaration do not affect performances even when + // ensure_check is false. + + // If copyable, copy self and args for postconditions. + DBC_ADD_POST_(typename, copyable_class_type) post_self(self); + // post post_arg0(arg0); ... + BOOST_PP_REPEAT(DBC_arg_num, DBC_post_var_decl, ~) +#endif // check + + // In following code, invariant is checked *before* + // require/ensure so pre/post condition assertions can relay + // on invariant to hold (e.g., if a precondition assertion was + // to dereference a ptr it could do so safely if inv asserted + // prt not NULL...). + + // Effective preconditions: invariant AND require. +#if DBC_CHECK_INVARIANT + if (invariant_check && pre_invariant_check) + { check_invariant(self); } +#endif // check +#if DBC_CHECK_REQUIRE + if (require_check) { + check_require(self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING( + DBC_arg_num, DBC_arg_name, ~) + ); + } +#endif // check + + // Body call. + DBC_LOG_DEBUG_(log << name() << "executing body"); + // If non-void "result_type result((self.*body)(arg0, ...));", + // else just call "(self.*body)(arg0, ...);" (no result). + BOOST_PP_EXPR_IF(DBC_is_not_void, result_type result) + BOOST_PP_LPAREN_IF(DBC_is_not_void) // "(" result constr. + (self.*body)( + // arg0, arg1, ... + BOOST_PP_ENUM(DBC_arg_num, DBC_arg_name, ~) + ) + BOOST_PP_RPAREN_IF(DBC_is_not_void) // ")" result contr. + ; + + // Effective postconditions: invariant AND ensure. +#if DBC_CHECK_INVARIANT + if (invariant_check && post_invariant_check) + { check_invariant(self); } +#endif // check +#if DBC_CHECK_ENSURE + if (ensure_check) { + check_ensure(post_self + // , post_arg0, post_arg1, ... + BOOST_PP_ENUM_TRAILING( + DBC_arg_num, DBC_post_var_name, ~) + // , result + BOOST_PP_COMMA_IF(DBC_is_not_void) + BOOST_PP_EXPR_IF(DBC_is_not_void, result) + ); + } +#endif // check + + DBC_LOG_DEBUG_(log << name() << "returning"); + // Casting resolves ambiguities with base class' dbc::object<>. + ((const dbc::object&)self) + .dbc_object_checking_contract_ = false; + + // If not void "return result;", else "return;" (no result). + return BOOST_PP_EXPR_IF(DBC_is_not_void, result); + } catch (...) { + // Casting resolves ambiguities with base class' dbc::object<>. + ((const dbc::object&)self) + .dbc_object_checking_contract_ = false; + throw; + } +#endif // check + } + + private: + inline std::string name() const { + std::string n = class_name_; + if ("" != fun_name_) { n += "::" + fun_name_ + "()"; } + if ("" != n) { n += ": "; } + return n; + } + + base_contract_type* base_contract_; + std::string class_name_; + std::string fun_name_; + bool required_; + }; // class mem + + +# ifdef DBC_is_void // Constructors cannot return value. +public: + /** + * Constructors do not check invariant together with preconditions. + * Invariant is checked only with postconditions. This is because before + * body is executed (i.e., when preconditions are checked) object is not + * fully created yet so invariant may not hold yet. + */ + template // Constructors cannot have base. + class constr: public mem > { + public: + /** @todo[LC] Statically assert: + * (1) C is not dbc::copyable<>. + */ + + typedef constr this_type; + typedef C copyable_class_type; + typedef typename dbc::remove_copyable_::type class_type; + + inline result_type operator()( + // Only add ref here (it's const only if it was already const). + typename boost::add_reference::type self + , typename mem >::mem_fun_type body + // , arg0_type arg0, arg1_type arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg, ~) + , const std::string& the_fun_name = "" + ) { + return call(self, body + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg_name, ~) + , the_fun_name + , false /* never check inv with preconditions */); + } + }; // class constr +# endif // DBC_is_void + +# ifdef DBC_is_void // Destructor cannot return value. +# if DBC_arg_num == 0 // Destructor cannot have arguments. +public: + /** + * Destructor only check invariant at entry. No precondition checking as + * there is no arugment. No postcondition and invariant checking at exit + * because there is no object any more (it has been destructed). + * Destructor must not throw to comply with STL exception safety + * requirements. Therefore, broken_destructor_invariant() is called if + * invariant fails. + */ + // Destr have no base (C++ will automatically invoke base's destr w/ its + // contract if it has one). + template + class destr: public mem > { + public: + /** @todo[LC] Statically assert: + * (1) C is not dbc::copyable<>. + */ + + typedef destr this_type; + typedef C copyable_class_type; + typedef typename dbc::remove_copyable_::type class_type; + + inline result_type operator()( + // Only add ref here (it's const only if it was already const). + typename boost::add_reference::type self + , typename mem >::mem_fun_type body + // , arg0_type arg0, arg1_type arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg, ~) + , const std::string& the_fun_name = "" + ) { + return call(self, body + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg_name, ~) + , the_fun_name + , true /* check inv with preconditions (at entry) */ + , false /* never check preconditions */ + , false /* never check inv with postconditions */ + , false /* never check postcond (and no post copy) */); + } + + // Make sure no check throws. + +#if DBC_CHECK_INVARIANT + inline void check_invariant( + DBC_ADD_CONST_REF_(typename, class_type) self) throw() { + try { mem >::check_invariant(self); } + catch (...) { broken_destructor_invariant(); } // Never throw. + } +#endif // check + +#if DBC_CHECK_REQUIRE + inline virtual void check_require( + DBC_ADD_CONST_REF_(typename, class_type) self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, DBC_arg_const_ref, ~) + ) throw() {} +#endif // check + +#if DBC_CHECK_ENSURE + inline virtual void check_ensure( + DBC_ADD_POST_CONST_REF_(typename, copyable_class_type) self + // , arg0, arg1, ... + BOOST_PP_ENUM_TRAILING(DBC_arg_num, + DBC_post_arg_const_ref, ~) + BOOST_PP_COMMA_IF(DBC_is_not_void) + BOOST_PP_EXPR_IF(DBC_is_not_void, + DBC_ADD_CONST_REF_(typename, result_type) result) + ) throw() {} +#endif // check + + private: + // Hide no require/ensure impl and make sure nothing throws (but can't + // define these throw() otherwise they won't properly impl their pure + // virtual base function). +#if DBC_CHECK_REQUIRE + inline void require( + DBC_ADD_CONST_REF_(typename, class_type) self) {} +#endif // check + +#if DBC_CHECK_ENSURE + inline void ensure( + DBC_ADD_POST_CONST_REF_(typename, copyable_class_type) self) {} +#endif // check + }; // class destr +# endif // DBC_arg_num +# endif // DBC_is_void + +}; // class fun + +} // namespace dbc + + // Undef ALL local macros. +# define DBC_post_var_decl +# define DBC_post_var_name +# define DBC_post_arg_const_ref +# define DBC_arg_const_ref +# define DBC_arg +# define DBC_arg_type +# define DBC_arg_name +# define DBC_is_not_spec +# define DBC_is_spec +# define DBC_is_not_void +# define DBC_is_void +# define DBC_arg_num +# define DBC_rest_arg_num + +// Self-Iteration Out-of-range (should never happen...) // + +#else +# error "DBC preprocessor iteration depth out-of-range (internal error)" + +#endif // BOOST_PP_IS_ITERATING + +#endif // DBC + diff --git a/include/dbc/fun_macro.hpp b/include/dbc/fun_macro.hpp new file mode 100755 index 00000000..ef9d37a7 --- /dev/null +++ b/include/dbc/fun_macro.hpp @@ -0,0 +1,113 @@ + +#ifndef DBC_FUN_MACRO_HPP_ +#define DBC_FUN_MACRO_HPP_ + +#include "config.hpp" + +#if defined DBC_DOC + +#define DBC_MEM_FUN(signature, require_code, ensure_code, body_code) \ + DBC_CONFIG_DOC_REQUIRE(require_code) \ + DBC_CONFIG_DOC_ENSURE(ensure_code) \ + body_code +#define DBC_CONSTRUCTOR(signature, require_code, ensure_code, body_code) \ + DBC_CONFIG_DOC_REQUIRE(require_code) \ + DBC_CONFIG_DOC_ENSURE(ensure_code) \ + body_code +#define DBC_DESTRUCTOR(signature, body_code) \ + /* destructor has no pre/post conditions */ \ + body_code + +#elif !defined DBC || \ + (!DBC_CHECK_REQUIRE && !DBC_CHECK_ENSURE && !DBC_CHECK_INVARIANT) + +#define DBC_MEM_FUN(signature, require_code, ensure_code, body_code) \ + body_code +#define DBC_CONSTRUCTOR(signature, require_code, ensure_code, body_code) \ + body_code +#define DBC_DESTRUCTOR(signature, body_code) \ + body_code + +#else // DBC + +#include "detail/pp/fun.hpp" +#include "detail/pp/fun_traits.hpp" +#include "detail/fun_macro_traits.hpp" +#include "body.hpp" +#include +#include + +// Public // + +#define DBC_MEM_FUN(signature, require_code, ensure_code, body_code) \ + DBC_MEM_FUN_FROM_TRAITS_(DBC_PP_MEM_FUN_(signature), mem, \ + require_code, ensure_code, body_code) + +/** Constructors don't check inv w/ precond -- no obj before body exec. */ +#define DBC_CONSTRUCTOR(signature, require_code, ensure_code, body_code) \ + DBC_MEM_FUN_FROM_TRAITS_(DBC_PP_CONSTRUCTOR_(signature), constr, \ + require_code, ensure_code, body_code) + +/** Destructors have no pre/post conditions -- they only check inv at entry. */ +#define DBC_DESTRUCTOR(signature, body_code) \ + DBC_MEM_FUN_FROM_TRAITS_(DBC_PP_DESTRUCTOR_(signature), destr, \ + {/* no preconditions */}, {/* no postconditions */}, body_code) + +// Private // + +/** Expand to code. */ +#define DBC_MEM_FUN_CODE_(err, f, inner_contract_type, \ + require_code, ensure_code, body_code) /**/ \ + /* fun invoking contract (this line follows user's fun declaration) */ \ + { \ + DBC_PP_FUN_GET_RETURN_(f) DBC_FUN_GET_CONTRACT_NAME_(f) \ + (/* invoke contract constructor */)(/* invoke operator() */ \ + *this, &DBC_PP_FUN_GET_CLASS_TYPE_(f) \ + ::DBC_BODY(DBC_PP_FUN_GET_NAME_(f)) \ + DBC_FUN_GET_TRAILING_ARG_NAMES_(f), \ + BOOST_PP_STRINGIZE(DBC_PP_FUN_GET_NAME_(f))); \ + } \ + /* contract */ \ +protected: \ + friend class DBC_FUN_GET_MEM_CONTRACT_TYPE_(f, inner_contract_type); \ + BOOST_PP_EXPR_IF(DBC_PP_FUN_IS_DERIVED_(f), friend class \ + ::dbc::post< DBC_FUN_GET_MEM_BASE_CONTRACT_TYPE_(f) >;) \ + class DBC_FUN_GET_CONTRACT_NAME_(f): \ + public DBC_FUN_GET_MEM_CONTRACT_TYPE_(f, inner_contract_type) { \ + BOOST_PP_EXPR_IF(DBC_CHECK_REQUIRE, \ + void require(DBC_FUN_GET_MEM_REQUIRE_ARGS_(f)) require_code) \ + BOOST_PP_EXPR_IF(DBC_CHECK_ENSURE, \ + void ensure(DBC_FUN_GET_MEM_ENSURE_ARGS_(f)) ensure_code) \ + }; \ + /* declare fun body */ \ + DBC_PP_FUN_GET_VIRTUAL_(f) DBC_PP_FUN_GET_RESULT_TYPE_(f) \ + DBC_BODY(DBC_PP_FUN_GET_NAME_(f))( \ + DBC_FUN_GET_BODY_ARGS_(f)) DBC_PP_FUN_GET_CONST_(f) body_code \ + /* restore original access level */ \ +DBC_PP_FUN_GET_ACCESS_(f): \ + /**/ + +/** Expand to pre-processor error. */ +#define DBC_MEM_FUN_ERROR_(err, f, inner_contract_type, \ + require_code, ensure_code, body_code) \ + /* Boost.Preprocessor errors BOOST_PP_ASSERT() and/or BOOST_PP_ASSERT_MSG() are best avoided here as they will not provide meaningfull error message (message will be visible only in preprocessed code if compiling with -E but complier error will not, in general, contain the error message. Boost.MPL static assertion allow instead to produce meaning error message at compile time. */ \ + ; /* closes previous statement (and no effect if no prev statement) */ \ + BOOST_MPL_ASSERT_MSG(0, BOOST_PP_TUPLE_ELEM(2, 1, err), ()); + +/** Make sure DBC_PP_FUN_() expands only once (does expensive parsing). */ +#define DBC_MEM_FUN_FROM_TRAITS_(sign_traits_err, inner_contract_type, \ + require_code, ensure_code, body_code) \ + BOOST_PP_IF(BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_TUPLE_ELEM( \ + 3, 2, sign_traits_err)), /* expand to error */ \ + DBC_MEM_FUN_ERROR_ \ + , /* else (no error, expand to code) */ \ + DBC_MEM_FUN_CODE_ \ + ) /* endif (expand selected macro)*/ \ + (BOOST_PP_TUPLE_ELEM(3, 2, sign_traits_err), \ + BOOST_PP_TUPLE_ELEM(3, 1, sign_traits_err), inner_contract_type, \ + require_code, ensure_code, body_code) \ + +#endif // DBC + +#endif // DBC_FUN_MACRO_HPP_ + diff --git a/include/dbc/invariant.hpp b/include/dbc/invariant.hpp new file mode 100755 index 00000000..2b37875d --- /dev/null +++ b/include/dbc/invariant.hpp @@ -0,0 +1,41 @@ + +#ifndef DBC_INVARIANT_HPP_ +#define DBC_INVARIANT_HPP_ + +#include "config.hpp" + +#if defined DBC_DOC + +#define DBC_INVARIANT(full_class_type, invariant_code) \ + DBC_CONFIG_DOC_INVARIANT(full_class_type, invariant_code) + +#elif !defined DBC + +#define DBC_INVARIANT(full_class_type, invariant_code) + +#else // DBC + +#include "object.hpp" +#include +#include +#include + +/** + * @param full_class_type Must include class's namespace and template params. + */ +#define DBC_INVARIANT(full_class_type, invariant_code) \ + /* statically assert "class is derived from dbc::object<>" */ \ + /* asserted here instead of in fun<> so user gets error in single spot */ \ + BOOST_MPL_ASSERT_MSG((::boost::is_base_of< ::dbc::object< \ + full_class_type >, full_class_type >::value), \ + DBC_ERROR_class_must_privately_inherit_from_dbc_object_, \ + (/* don't specify class_type here as it could be abstract */)); \ + /* class invariant impl (as non-member friend) */ \ + BOOST_PP_EXPR_IF(DBC_CHECK_INVARIANT, \ + friend inline void dbc_invariant_(const full_class_type& self) \ + invariant_code) + +#endif // DBC + +#endif // DBC_INVARIANT_HPP_ + diff --git a/include/dbc/loop.hpp b/include/dbc/loop.hpp new file mode 100755 index 00000000..2b96cba9 --- /dev/null +++ b/include/dbc/loop.hpp @@ -0,0 +1,88 @@ + +#ifndef DBC_LOOP_HPP_ +#define DBC_LOOP_HPP_ + +#ifdef DBC + +#include "assertion.hpp" +#include "detail/logging.hpp" +#include +#include + +namespace dbc { + +class loop { +protected: + inline void run(const char* file = "", const int& line = 0) { +#ifdef DBC_CHECK_LOOP + unsigned int old_var = std::numeric_limits::quiet_NaN(); + unsigned int count = 1; + check_invariant(count); + old_var = check_variant(old_var, count, file, line); +#endif // DBC_CHECK_LOOP + + while (!until()) { + body(); + +#ifdef DBC_CHECK_LOOP + check_invariant(count); + old_var = check_variant(old_var, count, file, line); + ++count; +#endif // DBC_CHECK_LOOP + + } // while() + } + + inline virtual void invariant() {} + + /** + * @return If std::numeric_limits::quiet_NaN() is returned, + * the variant has no effect (the variant condition will always be true). + */ + inline virtual unsigned int variant() + { return std::numeric_limits::quiet_NaN(); } + + virtual bool until() = 0; + + virtual void body() = 0; + +private: + unsigned int check_variant(const unsigned int& old_variant, + const unsigned int& count, const char* file, const int& line) { + unsigned int var = variant(); + try { + if (std::numeric_limits::quiet_NaN() != var && + std::numeric_limits::quiet_NaN() + != old_variant) { + bool var_violated = var < old_variant; // Must not decrease. + if (!var_violated || log_level_ >= LOG_LEVEL_VIOLATION) { + oassertionstream oas(var_violated, + "not-decreasing variant", file, line, ""); + oas << "variant " << var << " decreased from " + << old_variant << raise<>(); + } + } + } catch (condition_violation& ex) { + std::ostringstream oss; + oss << ex.what() << " (iteration #" << count << ")"; + throw variant_violation(oss.str()); + } + return var; + } + + void check_invariant(const unsigned int& count) { + try { invariant(); } + catch (condition_violation& ex) { + std::ostringstream oss; + oss << ex.what() << " (iteration #" << count << ")"; + throw invariant_violation(oss.str()); + } + } +}; + +} // namespace dbc + +#endif // DBC + +#endif // DBC_LOOP_HPP_ + diff --git a/include/dbc/mparam.hpp b/include/dbc/mparam.hpp new file mode 100755 index 00000000..41293d76 --- /dev/null +++ b/include/dbc/mparam.hpp @@ -0,0 +1,57 @@ + +#ifndef DBC_MPARAM_HPP_ +#define DBC_MPARAM_HPP_ + +#if defined DBC_DOC + +// Doxygen does not deal w/ Boost.Preprocessor well... using a simpler macro +// implementation that just selects the type (1st token). However, I am not +// sure if this will work well in all contexes Doxygen will expand this macro. +#define DBC_MPARAM(count, tokens) DBC_MPARAM_GET_1ST_ tokens + +#define DBC_MPARAM_GET_1ST_(first, rest) first + +#elif !defined DBC + +#undef DBC_MPARAM + +#else // DBC + +#include + +#define DBC_MPARAM_GET_TOKEN_(z, n, tokens_array) \ + BOOST_PP_ARRAY_ELEM(n, tokens_array) + +/** + * Concatenate specified tokens into a single macro parameter. + * This is usefull to pass templates and code that contain "," to the + * preprocessor. If a token containing a "," is passed to the preprocessor, it + * will be interpreted as two separate tokens unless wrapped by this macro. + * If the macro parameter you are passing will be used by the called macro as a + * value then just wrapping it with extra parenthesis () will work and this + * macro is not needed. However, the extra parenthersis will not work if the + * parameter is used as a type in which case you need to use this macro. It is + * safe to always use this macro for any sort of macro parameter (value, type, + * code, etc). + * @code + * // Note the *required* extra parenthesis around map<>. + * class ii DBC_INHERIT_OBJECT(DBC_MPARAM(2, (std::map))) { ... } + * @endcode + * @param[in] count Number of tokens {0, 1, ..., n}. If coutn is 0, this macro + * expans to nothing. If count is 1 this macro works but since you only have + * one token you can just pass it as macro parameter without using this macro. + * For count greater or equal than 1, this macro is necessary. + * @param[in] tokens Tokens to concatenate into a simple macro parameter. + * Tokens must be passed as a Boost.Preprocessor tuple of size equal to count + * "(token0, token1, ..., token_count-1)". This simply means that you have to + * wrap your comma seperated token expression with an extra pair of + * parenthesis () as shown in the example above. + * @return Concatenated tokens (token0, token1, ..., token_count-1). + */ +#define DBC_MPARAM(count, tokens) \ + BOOST_PP_ENUM(count, DBC_MPARAM_GET_TOKEN_, (count, tokens) /* array */) + +#endif // DBC + +#endif // DBC_MPARAM_HPP_ + diff --git a/include/dbc/object.hpp b/include/dbc/object.hpp new file mode 100755 index 00000000..fe2a568f --- /dev/null +++ b/include/dbc/object.hpp @@ -0,0 +1,57 @@ + +#ifndef DBC_OBJECT_HPP_ +#define DBC_OBJECT_HPP_ + +#ifndef DBC + +#define DBC_INHERIT_OBJECT(class_type) +#define DBC_TRAILING_OBJECT(class_type) + +#else // DBC + +#include "detail/threading/sync.hpp" + +#define DBC_INHERIT_OBJECT(class_type) : private ::dbc::object< class_type > +#define DBC_TRAILING_OBJECT(class_type) , private ::dbc::object< class_type > + +namespace dbc { + +/** + * This is a template on class type C so no common base class is introduced. + */ +template +class object { +public: + /** + * True iff *this object* is checking a contract. The name contains + * "object" to better differentiate the role of this flag compared to the + * role of the dbc::globally_checking_contract_ flag. + * dbc_ prefix not to clash with C's names, _ postfix to reserve it. + * Mutable so contract can access it via self (of const class_type&). + */ + mutable sync_ dbc_object_checking_contract_; + + // Don't decl these constr explicit (they must also support possible + // implicit type conversions of user's class, if any). + + /* no explicit */ object(): dbc_object_checking_contract_(false) {} + + /* no explicit */ object(const object& other): + dbc_object_checking_contract_(other.dbc_object_checking_contract_) + {} + + const object& operator=(const object& other) { + dbc_object_checking_contract_ = dbc_object_checking_contract_ || + other.dbc_object_checking_contract_; + return *this; + } + + virtual ~object() {} +}; + +} // namespace dbc + +#endif // DBC + +#endif // DBC_OBJECT_HPP_ + diff --git a/include/dbc/post.hpp b/include/dbc/post.hpp new file mode 100755 index 00000000..53626cdd --- /dev/null +++ b/include/dbc/post.hpp @@ -0,0 +1,58 @@ + +#ifndef DBC_POST_HPP_ +#define DBC_POST_HPP_ + +#ifdef DBC + +#include "copyable.hpp" +#include "detail/logging.hpp" +#include "detail/type_traits.hpp" + +#define DBC_ADD_POST_(typename_keyword, the_type) \ + ::dbc::post::type> + +#define DBC_ADD_POST_CONST_REF_(typename_keyword, the_type) \ + const DBC_ADD_POST_(typename_keyword, the_type)& + +namespace dbc { + +/** Primary template, does not assume T is copyable (old unavailable). */ +template +class post { +public: + const typename remove_const_ref_::type& now; // A ref (no obj copy). + + explicit post(const typename remove_const_ref_::type& orig): + now(orig) {} + + template + explicit post(const D& derived): now(derived.now) {} +}; + +/** Specialization for copyable T (old is available). */ +template +class post > { +public: + const typename remove_const_ref_::type old; // Not a ref, will copy obj! + const typename remove_const_ref_::type& now; // A ref (no obj copy). + + explicit post(const typename remove_const_ref_::type& orig): + old(orig), now(orig) { + DBC_LOG_DEBUG_(log << "post<" << typeid(T).name() + << ">: copied from original object"); + } + + template + explicit post(const D& derived): old(derived.old), now(derived.now) { + DBC_LOG_DEBUG_(log << "post< " << typeid(T).name() + << ">: copied from derived object of type " + << typeid(D).name()); + } +}; + +} // namespace dbc + +#endif // DBC + +#endif // DBC_POST_HPP_ + diff --git a/include/dbc/pp_define.hpp b/include/dbc/pp_define.hpp new file mode 100755 index 00000000..de5466b2 --- /dev/null +++ b/include/dbc/pp_define.hpp @@ -0,0 +1,76 @@ + +#ifndef DBC_PP_DEFINE_HPP_ +#define DBC_PP_DEFINE_HPP_ + +// Compilation Symbols (must #include before any other library #include. + +// Only very basic consistency checking on control macro #definition is done +// here -- the rest is left up to the user. E.g., probably makes no sense to +// #define DBC_CHECK_REQUIRE without #defining also DBC but this is not +// enforced here. + +#if defined DBC_DOC && defined DBC_ALL +# error "DBC_DOC cannot be #defined at the same time with DBC and/or DBC_ALL" +#endif + +#ifdef DBC_NO // Shortcut for all DBC_CHECK off. +# ifdef DBC_ALL +# error "DBC_NO and DBC_ALL cannot be #defined at the same time" +# endif +# undef DBC_CHECK_REQUIRE +# undef DBC_CHECK_ENSURE +# undef DBC_CHECK_INVARIANT +# undef DBC_CHECK_LOOP +#endif + +#ifdef DBC_ALL // Shortcut for all DBC_CHECK on. +# ifdef DBC_NO +# error "DBC_ALL and DBC_NO cannot be #defined at the same time" +# endif +# define DBC_CHECK_REQUIRE +# define DBC_CHECK_ENSURE +# define DBC_CHECK_INVARIANT +# define DBC_CHECK_LOOP +#endif + +#ifdef DBC // DBC symbol controls user's code compilation. +# error "DBC is #defined internally and it should not be #defined by the user -- use DBC_ALL, DBC_NO, DBC_CHECK_REQUIRE, DBC_CHECK_ENSURE, and/or DBC_CHECK_INVARIANT instead" +#elif defined DBC_CHECK_REQUIRE || defined DBC_CHECK_ENSURE \ + || defined DBC_CHECK_INVARIANT // DBC_CHECK_LOOP must be separate! +# define DBC +#else +# undef DBC +#endif + +// Make sure checks #defined as 0/1 (to use them with Boost.Preprocessor if). + +#ifdef DBC_CHECK_REQUIRE +# undef DBC_CHECK_REQUIRE +# define DBC_CHECK_REQUIRE 1 +#else +# define DBC_CHECK_REQUIRE 0 +#endif + +#ifdef DBC_CHECK_ENSURE +# undef DBC_CHECK_ENSURE +# define DBC_CHECK_ENSURE 1 +#else +# define DBC_CHECK_ENSURE 0 +#endif + +#ifdef DBC_CHECK_INVARIANT +# undef DBC_CHECK_INVARIANT +# define DBC_CHECK_INVARIANT 1 +#else +# define DBC_CHECK_INVARIANT 0 +#endif + +#ifdef DBC_CHECK_LOOP +# undef DBC_CHECK_LOOP +# define DBC_CHECK_LOOP 1 +#else +# define DBC_CHECK_LOOP 0 +#endif + +#endif // DBC_PP_DEFINE_HPP_ + diff --git a/test/Checking/checking.cpp b/test/Checking/checking.cpp new file mode 100755 index 00000000..f9d0e9c2 --- /dev/null +++ b/test/Checking/checking.cpp @@ -0,0 +1,16 @@ + +// Makefile compiles this code with different DBC_CHECKING... #definitions. + +// Always verbose logging for this test (so you can see what is checked). +#define DBC_CONFIG_LOG_LEVEL DBC_LOG_LEVEL_ALL + +#include "number.hpp" +#include + +int main() { + number i(10); + std::cout << (i / 4.321) << std::endl; + + return 0; +} + diff --git a/test/Checking/number.hpp b/test/Checking/number.hpp new file mode 100755 index 00000000..3a42e8bb --- /dev/null +++ b/test/Checking/number.hpp @@ -0,0 +1,56 @@ + +#include + +template +class number DBC_INHERIT_OBJECT(number) { +public: + number(const T& value = T()): value_ptr_(new T()) { init(value); } + + virtual ~number() + DBC_DESTRUCTOR( (public) (virtual) (template)(number)(), ; ) + + T get() const + DBC_MEM_FUN( (public) (T) (template)(number) (get)() (const), { + }, { + DBC_ASSERT(result == *(self.now.value_ptr_), "returning value"); + }, ; ) + + double operator/(const double& den) const + DBC_MEM_FUN( (public) (double) (template)(number) + (operator_div)( (const double&)(den) ) (const), { + DBC_ASSERT(den != 0, "non-zero denumerator"); + }, { + DBC_ASSERT((result * den.now) == self.now.get(), "returning division"); + }, ; ) + +private: + void init(const T& value) + DBC_CONSTRUCTOR( (private) (template)(number)( (const T&)(value) ), { + }, { + DBC_ASSERT(self.now.get() == value.now, "value set"); + }, ; ) + + T* value_ptr_; + + DBC_INVARIANT(number, { + DBC_ASSERT(self.value_ptr_, "value exists"); + }) +}; + +// Implementation // + +template +void number::DBC_BODY(init)(const T& value) { *value_ptr_ = value; } + +template +DBC_DESTRUCTOR_BODY(number)() { delete value_ptr_; } + +template +T number::DBC_BODY(get)() const { return *value_ptr_; } + +template +double number::DBC_OPERATOR_BODY(operator/, operator_div)( + const double& den) const { + return get() / den; +} + diff --git a/test/Construction/construction.cpp b/test/Construction/construction.cpp new file mode 100755 index 00000000..d63e230a --- /dev/null +++ b/test/Construction/construction.cpp @@ -0,0 +1,82 @@ + +#include +#include +#include + +// Specification // + +template +class number DBC_INHERIT_OBJECT(number) { +public: + // Method (1): Constr spec and impl in .hpp (w/ and w/out DBC). + explicit number(const T& value = T()): value_ptr_(new T()) + DBC_CONSTRUCTOR( (public) (template)(number)( (const T&)(value) ), { + DBC_ASSERT(true, "constructor precondition"); + }, { + DBC_ASSERT(true, "constructor postondition"); + }, { + *value_ptr_ = value; + }) + + // Method (2): To split spec and impl must use init(). + // Contract and construcotr real implementation specified by init(). Actual + // constructor has trivial implementation that simply calls init() but base + // initializers (if any) must be specified by the constructor + // implementation (this trivial constructor implementation is simple and + // small enough that could be defined directly in the .hpp but it could be + // put in the .cpp as well -- it is mainly a matter of taste.) + // This private init() mechanism is commonly used also to implement + // delegating construction in C++. To avoid initialization problems: + // (a) init() must be private, (b) init() must be called only by the class + // constructor and not by any other member function. If C++ supported + // delegating constructor, DBC++ could have avoided the init() + // complication... + explicit number(const std::string& s): value_ptr_(new T()) { init(s); } + + virtual ~number() + DBC_DESTRUCTOR( (public) (virtual) (template)(number)(), ;) + + const T& value() const + DBC_MEM_FUN( (public) (const T&) (template)(number) (value)() (const), { + }, { + DBC_ASSERT(result == *(self.now.value_ptr_), "returning value"); + }, ;) + +private: + void init(const std::string& s) + DBC_CONSTRUCTOR( (private) (template)(number)( (const std::string&)(s) ), { + DBC_ASSERT(true, "init precondition"); + }, { + // value_ptr_ can be safety deref. here because invariant checked it. + DBC_ASSERT(T(s.now.size()) == *(self.now.value_ptr_), + "value initialized"); + }, ;) + + T* value_ptr_; + + DBC_INVARIANT(number, { + DBC_ASSERT(self.value_ptr_, "value exists"); + }) +}; + +// Implementation // + +template +void number::DBC_BODY(init)(const std::string& s) + { *value_ptr_ = s.size(); } + +template +DBC_DESTRUCTOR_BODY(number)() { delete value_ptr_; } + +template +const T& number::DBC_BODY(value)() const { return *value_ptr_; } + +// Program // + +int main() { + std::cout << number(10).value() << std::endl; + std::cout << number("abc").value() << std::endl; + + return 0; +}; + diff --git a/test/Doc/doc.cpp b/test/Doc/doc.cpp new file mode 100755 index 00000000..953d8659 --- /dev/null +++ b/test/Doc/doc.cpp @@ -0,0 +1,71 @@ + +#include +#include + +namespace util { + +/** + * Example of template documentation with namespace and more than 1 template + * argument. + * @tparam From Source type. + * @tparam To Destination type. + */ +template +class converter DBC_INHERIT_OBJECT(DBC_MPARAM(2, (converter))) { +public: + /** + * Convert specified value to integer. + * @param[in] from The value to convert. + * @return Converted value. + */ + To convert(const From& from) + DBC_MEM_FUN( (public) (To) (template)(converter) + (convert)( (const From&)(from) ), { + DBC_ASSERT(true, "convert precondition"); + }, { + DBC_ASSERT(true, "convert postcondition"); + }, { + return To(from); + }) + +private: + // Here, class type must spell full namespace for Doxygen to work. + DBC_INVARIANT(DBC_MPARAM(2, (util::converter)), { + DBC_ASSERT(true, "converter invariant"); + }) +}; + +} // namespace util + +/** Example fo class documentation (no template, no namespace). */ +class display DBC_INHERIT_OBJECT(display) { +public: + /** + * Print specified integer on stdout and terminate with a new line. + * @param[in] i Integer to print. + */ + void print(const int& i) const + DBC_MEM_FUN( (public) (void) (display) + (print)( (const int&)(i) ) (const), { + DBC_ASSERT(true, "print precondition"); + }, { + DBC_ASSERT(true, "print postcondition"); + },{ + std::cout << i << std::endl; + }) + +private: + // Don't use global namespace prefix "::display", it will confuse Doxygen. + DBC_INVARIANT(display, { + DBC_ASSERT(true, "display invariant"); + }) +}; + +int main() { + util::converter c; + display d; + d.print(c.convert(3.2)); + + return 0; +} + diff --git a/test/Operators/operators.cpp b/test/Operators/operators.cpp new file mode 100755 index 00000000..c1109286 --- /dev/null +++ b/test/Operators/operators.cpp @@ -0,0 +1,65 @@ + +#include +#include + +// Specification // + +template +class number DBC_INHERIT_OBJECT(number) { +public: + explicit number(const T& value = T()): value_(value) + DBC_CONSTRUCTOR( (public) (template)(number)( (const T&)(value) ), { + }, { + DBC_ASSERT(self.now.value_ == value.now, "value set"); + }, {}) + + // Operator body can be split from contract specification but using + // valid macro token for function name and DBC_OPERATOR_BODY() instead of + // DBC_BODY() -- when using macro APIs. + T operator/(const T& denumerator) const + DBC_MEM_FUN( (public) (T) (template)(number) + (operator_div)( (const T&)(denumerator) ) (const), { + DBC_ASSERT(denumerator != 0, "denumerator not zero"); + }, { + DBC_ASSERT(result * denumerator.now == self.now.value_, "divided"); + }, ;) + + // LIMITATION: This operator has no return type so its body must be + // implemented here together with its contract specification -- when using + // macro APIs. + operator T() const + DBC_MEM_FUN( (public) (T) (template)(number) + (operator_T)() (const), { + }, { + DBC_ASSERT(result == self.now.value_, "returning value"); + }, { + return value_; + }) + +private: + T value_; + + DBC_INVARIANT(number, {}) +}; + +// Implementation // + +template +T number::DBC_OPERATOR_BODY(operator/, operator_div)( + const T& denumerator) const { + return value_ / denumerator; +} + +// Program // + +int main() { + number i(+20); + + number d1(i / -10); + std::cout << d1 << std::endl; + + number d2(i / 0); // Will violate operator/() precondition. + + return 0; +}; + diff --git a/test/README.txt b/test/README.txt new file mode 100755 index 00000000..d24e42d3 --- /dev/null +++ b/test/README.txt @@ -0,0 +1 @@ +Programs to test different library functionalities. diff --git a/test/Subcontracting/base_class.cpp b/test/Subcontracting/base_class.cpp new file mode 100755 index 00000000..2f187cfb --- /dev/null +++ b/test/Subcontracting/base_class.cpp @@ -0,0 +1,12 @@ + +#include "base_class.hpp" +#include + +int main() { + base b; + std::cout << b.f(-1) << std::endl; + std::cout << b.f_m(-1) << std::endl; + + return 0; +}; + diff --git a/test/Subcontracting/base_class.hpp b/test/Subcontracting/base_class.hpp new file mode 100755 index 00000000..0e41c7d4 --- /dev/null +++ b/test/Subcontracting/base_class.hpp @@ -0,0 +1,44 @@ + +#ifndef BASE_CLASS_HPP_ +#define BASE_CLASS_HPP_ + +#include + +class base DBC_INHERIT_OBJECT(base) { +public: + virtual int f(const int& x) const +#ifdef DBC + { return dbc_f_x()(*this, &base::DBC_BODY(f), x, "f"); } +protected: + class dbc_f_x: public dbc::fun::mem { + void require(const base& self, const int& x) { + DBC_ASSERT(true, "base precondition"); + } + void ensure(const dbc::post& self, const dbc::post& x, + const int& result) { + DBC_ASSERT(true, "base postcondition"); + } + }; + virtual int DBC_BODY(f)(const int& x) const +#endif + { return x; } +public: + + virtual int f_m(const int& x) const + DBC_MEM_FUN( (public) (virtual) (int) (base) + (f_m)( (const int&)(x) ) (const), { + DBC_ASSERT(true, "macro base precondition"); + }, { + DBC_ASSERT(true, "macro base postcondition"); + }, { + return x; + }) + +private: + DBC_INVARIANT(base, { + DBC_ASSERT(true, "base invariant"); + }) +}; + +#endif // BASE_CLASS_HPP_ + diff --git a/test/Subcontracting/base_template.cpp b/test/Subcontracting/base_template.cpp new file mode 100755 index 00000000..af4c0aac --- /dev/null +++ b/test/Subcontracting/base_template.cpp @@ -0,0 +1,12 @@ + +#include "base_template.hpp" +#include + +int main() { + base b; + std::cout << b.f(-1) << std::endl; + std::cout << b.f_m(-1) << std::endl; + + return 0; +}; + diff --git a/test/Subcontracting/base_template.hpp b/test/Subcontracting/base_template.hpp new file mode 100755 index 00000000..fbc29e8f --- /dev/null +++ b/test/Subcontracting/base_template.hpp @@ -0,0 +1,46 @@ + +#ifndef BASE_TEMPLATE_HPP_ +#define BASE_TEMPLATE_HPP_ + +#include + +template +class base DBC_INHERIT_OBJECT(base) { +public: + virtual T f(const T& x) const +#ifdef DBC + { return dbc_f_x()(*this, &base::DBC_BODY(f), x, "f"); } +protected: + class dbc_f_x: public dbc::fun::template + mem::this_type { + void require(const base& self, const T& x) { + DBC_ASSERT(true, "base precondition"); + } + void ensure(const dbc::post& self, const dbc::post& x, + const T& result) { + DBC_ASSERT(true, "base postcondition"); + } + }; + virtual T DBC_BODY(f)(const T& x) const +#endif + { return x; } +public: + + virtual T f_m(const T& x) const + DBC_MEM_FUN( (public) (virtual) (T) (template)(base) + (f_m)( (const T&)(x) ) (const), { + DBC_ASSERT(true, "macro base precondition"); + }, { + DBC_ASSERT(true, "macro base postcondition"); + }, { + return x; + }) + +private: + DBC_INVARIANT(base, { + DBC_ASSERT(true, "base invariant"); + }) +}; + +#endif // BASE_TEMPLATE_HPP_ + diff --git a/test/Subcontracting/class_subcontracts_class.cpp b/test/Subcontracting/class_subcontracts_class.cpp new file mode 100755 index 00000000..f76e6c82 --- /dev/null +++ b/test/Subcontracting/class_subcontracts_class.cpp @@ -0,0 +1,12 @@ + +#include "class_subcontracts_class.hpp" +#include + +int main() { + derived d; + std::cout << d.f(-10) << std::endl; + std::cout << d.f_m(-10) << std::endl; + + return 0; +}; + diff --git a/test/Subcontracting/class_subcontracts_class.hpp b/test/Subcontracting/class_subcontracts_class.hpp new file mode 100755 index 00000000..0ab4d459 --- /dev/null +++ b/test/Subcontracting/class_subcontracts_class.hpp @@ -0,0 +1,46 @@ + +#ifndef CLASS_SUBCONTRACTS_CLASS_HPP_ +#define CLASS_SUBCONTRACTS_CLASS_HPP_ + +#include "base_class.hpp" +#include + +class derived: public base DBC_TRAILING_OBJECT(derived) { +public: + int f(const int& x) const +#ifdef DBC + { return dbc_f_x()(*this, &derived::DBC_BODY(f), x, "f"); } +protected: + class dbc_f_x: public dbc::fun:: + mem { + void require(const derived& self, const int& x) { + DBC_ASSERT(false, "derived precondition -- but base's pass"); + } + void ensure(const dbc::post& self, const dbc::post& x, + const int& result) { + DBC_ASSERT(true, "derived postcondition"); + } + }; + int DBC_BODY(f)(const int& x) const +#endif + { return -x; } +public: + + int f_m(const int& x) const + DBC_MEM_FUN( (public) (int) (derived)DBC_BASE(base) + (f_m)( (const int&)(x) ) (const), { + DBC_ASSERT(false, "macro derived precondition -- but base's pass"); + }, { + DBC_ASSERT(true, "macro derived postcondition"); + }, { + return -x; + }) + +private: + DBC_INVARIANT(derived, { + DBC_ASSERT(true, "derived invariant"); + }) +}; + +#endif // CLASS_SUBCONTRACTS_CLASS_HPP_ + diff --git a/test/Subcontracting/class_subcontracts_template.cpp b/test/Subcontracting/class_subcontracts_template.cpp new file mode 100755 index 00000000..355df3ae --- /dev/null +++ b/test/Subcontracting/class_subcontracts_template.cpp @@ -0,0 +1,12 @@ + +#include "class_subcontracts_template.hpp" +#include + +int main() { + derived d; + std::cout << d.f(-10) << std::endl; + std::cout << d.f_m(-10) << std::endl; + + return 0; +}; + diff --git a/test/Subcontracting/class_subcontracts_template.hpp b/test/Subcontracting/class_subcontracts_template.hpp new file mode 100755 index 00000000..c175109b --- /dev/null +++ b/test/Subcontracting/class_subcontracts_template.hpp @@ -0,0 +1,53 @@ + +#ifndef CLASS_SUBCONTRACTS_TEMPLATE_HPP_ +#define CLASS_SUBCONTRACTS_TEMPLATE_HPP_ + +#include "base_template.hpp" +#include + +class derived: public base DBC_TRAILING_OBJECT(derived) { +public: + int f(const int& x) const +#ifdef DBC + { return dbc_f_x()(*this, &derived::DBC_BODY(f), x, "f"); } +protected: + class dbc_f_x: public dbc::fun:: + mem::dbc_f_x> { + void require(const derived& self, const int& x) { + DBC_ASSERT(false, "derived precondition -- but base's pass"); + } + void ensure(const dbc::post& self, const dbc::post& x, + const int& result) { + DBC_ASSERT(true, "derived postcondition"); + } + }; + int DBC_BODY(f)(const int& x) const +#endif + { + // IMPORTANT: When invoking parent's function directly, you must use + // the DBC_BODY() qualifier. Otherwise, the contract will recursively + // be checked again and again forever... + /** @todo[LC] If I use "return base::f(-x);" w/out the DBC_BODY() + * I get infinite recursion. Understand this issue better. */ + return base::DBC_BODY(f)(-x); + } +public: + + int f_m(const int& x) const + DBC_MEM_FUN( (public) (int) (derived)DBC_BASE(base) + (f_m)( (const int&)(x) ) (const), { + DBC_ASSERT(false, "macro derived precondition -- but base's pass"); + }, { + DBC_ASSERT(true, "macro derived postcondition"); + }, { + return -x; + }) + +private: + DBC_INVARIANT(derived, { + DBC_ASSERT(true, "derived invariant"); + }) +}; + +#endif // CLASS_SUBCONTRACTS_TEMPLATE_HPP_ + diff --git a/test/Subcontracting/template_subcontracts_class.cpp b/test/Subcontracting/template_subcontracts_class.cpp new file mode 100755 index 00000000..075138f6 --- /dev/null +++ b/test/Subcontracting/template_subcontracts_class.cpp @@ -0,0 +1,12 @@ + +#include "template_subcontracts_class.hpp" +#include + +int main() { + derived d; + std::cout << d.f(-10) << std::endl; + std::cout << d.f_m(-10) << std::endl; + + return 0; +} + diff --git a/test/Subcontracting/template_subcontracts_class.hpp b/test/Subcontracting/template_subcontracts_class.hpp new file mode 100755 index 00000000..1c4b751f --- /dev/null +++ b/test/Subcontracting/template_subcontracts_class.hpp @@ -0,0 +1,47 @@ + +#ifndef TEMPLATE_SUBCONTRACTS_CLASS_HPP_ +#define TEMPLATE_SUBCONTRACTS_CLASS_HPP_ + +#include "base_class.hpp" +#include + +template +class derived: public base DBC_TRAILING_OBJECT(derived) { +public: + T f(const T& x) const +#ifdef DBC + { return dbc_f_x()(*this, &derived::DBC_BODY(f), x, "f"); } +protected: + class dbc_f_x: public dbc::fun::template + mem::this_type { + void require(const derived& self, const T& x) { + DBC_ASSERT(false, "derived precondition -- but base's pass"); + } + void ensure(const dbc::post& self, const dbc::post& x, + const T& result) { + DBC_ASSERT(true, "derived postcondition"); + } + }; + T DBC_BODY(f)(const T& x) const +#endif + { return -x; } +public: + + T f_m(const T& x) const + DBC_MEM_FUN( (public) (T) (template)(derived)DBC_BASE(base) + (f_m)( (const T&)(x) ) (const), { + DBC_ASSERT(false, "macro derived precondition -- but base's pass "); + }, { + DBC_ASSERT(true, "macro derived postcondition"); + }, { + return -x; + }) + +private: + DBC_INVARIANT(derived, { + DBC_ASSERT(true, "derived invariant"); + }) +}; + +#endif // TEMPLATE_SUBCONTRACTS_CLASS_HPP_ + diff --git a/test/Subcontracting/template_subcontracts_template.cpp b/test/Subcontracting/template_subcontracts_template.cpp new file mode 100755 index 00000000..0850c03d --- /dev/null +++ b/test/Subcontracting/template_subcontracts_template.cpp @@ -0,0 +1,12 @@ + +#include "template_subcontracts_template.hpp" +#include + +int main() { + derived d; + std::cout << d.f(-10) << std::endl; + std::cout << d.f_m(-10) << std::endl; + + return 0; +} + diff --git a/test/Subcontracting/template_subcontracts_template.hpp b/test/Subcontracting/template_subcontracts_template.hpp new file mode 100755 index 00000000..120fea08 --- /dev/null +++ b/test/Subcontracting/template_subcontracts_template.hpp @@ -0,0 +1,47 @@ + +#ifndef TEMPLATE_SUBCONTRACTS_TEMPLATE_HPP_ +#define TEMPLATE_SUBCONTRACTS_TEMPLATE_HPP_ + +#include "base_template.hpp" +#include + +template +class derived: public base DBC_TRAILING_OBJECT(derived) { +public: + T f(const T& x) const +#ifdef DBC + { return dbc_f_x()(*this, &derived::DBC_BODY(f), x, "f"); } +protected: + class dbc_f_x: public dbc::fun::template + mem::dbc_f_x>::this_type { + void require(const derived& self, const T& x) { + DBC_ASSERT(false, "derived precondition -- but base's pass"); + } + void ensure(const dbc::post& self, const dbc::post& x, + const T& result) { + DBC_ASSERT(true, "derived postcondition"); + } + }; + T DBC_BODY(f)(const T& x) const +#endif + { return -x; } +public: + + T f_m(const T& x) const + DBC_MEM_FUN( (public) (T) (template)(derived)DBC_BASE(base) + (f_m)( (const T&)(x) ) (const), { + DBC_ASSERT(false, "macro derived precondition -- but base's pass "); + }, { + DBC_ASSERT(true, "macro derived postcondition"); + }, { + return -x; + }) + +private: + DBC_INVARIANT(derived, { + DBC_ASSERT(true, "derived invariant"); + }) +}; + +#endif // TEMPLATE_SUBCONTRACTS_TEMPLATE_HPP_ +