This describes the installation for development versions of Catsfoot. The run-time shared library contains only testing tools and does not need to be shipped with the software.
Nightly build versions are available from page Download.
There are nightly build packages for
Those can be installed with rpm
or dpkg
depending on the distribution.
Gentoo users can install it with:
# emerge -uv layman # sed -i "/^overlays *:/a http://vfrd-overlay.googlecode.com/svn/trunk/Documentation/vfrd-overlay.xml" /etc/layman/layman.cfg # layman -a vfrd # echo "dev-libs/catsfoot ~$(portageq envvar ARCH)" >>"${EPREFIX}/etc/portage/package.keywords" # emerge -u dev-libs/catsfoot
Catsfoot uses Autoconf and Automake, so the installation is pretty standard.
$ xz -dc /path/to/catsfoot-0.1.tar.gz | tar xf -
$ ./catsfoot-0.1/configure --prefix="/path/where/to/install"
$ make install
We discourage using the version of the repository as some versions might not have passed the test suite. Use a nightly build source package instead.
Catsfoot installs two pkg-config
package descriptions:
catsfoot
is used only for supporting concepts. It is used for shipped binaries of your project. In this way it does not depend on any run-time library, but still provides static concept checking.catsfoot-rt
is used for your test suite. It links to Catsfoot's runtime library.In your configure.ac file, you should have a line such as:
PKG_CHECK_MODULES([CF_TESTING], [catsfoot-rt]) PKG_CHECK_MODULES([CF_RELEASED], [catsfoot])
Then in your Makefile.am file, should have for example (for "check" program "mytest"):
mytest_CXXFLAGS=$(CF_TESTING_CFLAGS) mytest_LDFLAGS=$(CF_TESTING_LIBS)
The manual page of pkg-config
contains more information.
Do not forget to call "configure" with "CXX='g++ -std=c++0x'", or make your configure script to add "-std=c++0x" automatically.
Scons wiki page PkgConfig explains how to use pkg-config in your project.
For example:
g++-4.6.0 -std=c++0x `pkg-config --cflags --libs catsfoot-rt` yourtest.cc
Lets write a concept for a set. We can insert an element inside the set, and we can test whether an element has been inserted.
# include <catsfoot.hh> // We define some tools we will need in our concept. // - has_member_element_type<T> checks for member type "T::element_type" DEF_TYPE_MEMBER_PREDICATE(element_type); // - member_has(cv-qualifier T&, Args...) is an alias to // "T::has(Args...) cv-qualifier" DEF_MEMBER_WRAPPER(has); // - member_insert(cv-qualifier T&, Args...) is an alias to // "T::insert(Args...) cv-qualifier" DEF_MEMBER_WRAPPER(insert); namespace cf = catsfoot; // We define a concept "set" for a type we call "Set" template <typename Set> struct set: public cf::concept { // We define "element_type" to be an alias to Set::element_type if // it ever exists. Using has_member_element_type makes sure that // we will not get any strange error message in case Set::element_type // was not a type, or was not declared. // This alias makes the concept more concise, but is not necessary. typedef typename has_member_element_type<Set>::member_type element_type; // is_callable<C> is a predicate. We declare some aliases to the // predicate calling members Set::has(element_type) const, and // Set::insert(element_type). Since the predicates will be // reused several times, it helps us to make the concept definition // more concise. But it is not necessary. typedef cf::is_callable<member_has(const Set&, element_type)> has; typedef cf::is_callable<member_insert(Set&, element_type)> insert; // We declare our static requirements typedef cf::concept_list< // Set::element_type must exist and define a type. has_member_element_type<Set>, // Set::has(element_type) const must be callable has, // Its result should be implicitly convertible to bool. std::is_convertible<typename has::result_type, bool>, // Set::insert(element_type) must be callable insert, // We require "operator==(element_type, element_type)" to be defined cf::equality<element_type> > requirements; // For all element e and f, with a reference to set s, this axiom should // hold. static void insertion(const element_type& e, const element_type& f, Set& s) { // Was f already inside? bool res = s.has(f); s.insert(e); if (e != f) // if e is not equal to f, then insertion of e should insert nor // remove f. axiom_assert(res == s.has(f)); if (e == f) // if e and f are equal, then f should be in the set. axiom_assert(true == s.has(f)); } // Axioms are usually just static members. We need to declare them as // axioms for the test driver to use them. AXIOMS(insertion); };
Note that this concept is does not specify everything from the commonly used set implementations. For example, according to the axioms, a type where has
is always true for any element and where insert
does not perform any operation, will model the concept set
.
Let's now define a very simple implementation of set
. You can note that we can reuse any set type as long as it implements at least concept set
. The type will actually implement a more specific algebra.
template <typename T> struct slow_set { public: // Required by the concept. typedef T element_type; // Although this is not required by the concept, we provide // some constructors. slow_set() = default; slow_set(const slow_set&) = default; slow_set(std::initializer_list<T>); // Required by the concept void insert(const T&); bool has(const T&) const; private: std::vector<T> values; // Because: // - we do not resize the vector, we do not need default constructible // - we do not use move semantic, we can require copy // - we do not replace, we do not need assignment typedef cf::class_assert_concept< cf::concept_list<cf::is_constructible<T(const T&)>, cf::equality<T> > > requirements; }; template <typename T> void slow_set<T>::insert(const T& t) { for (auto i : values) { if (i == t) return; } values.push_back(t); } template <typename T> bool slow_set<T>::has(const T& t) const { for (auto i : values) { std::cerr << t << " == " << i << std::endl; if (i == t) return true; } return false; } template <typename T> slow_set<T>::slow_set(std::initializer_list<T> l) { for (auto i : l) { this->insert(i); } }
We now want to claim that for any T, slow_set<T> is a set. We can claim this because we made sure that slow_set<T> was not instantiated with an incompatible type through instantiation of slow_set<T>::requirements. The claim is made through type traits.
namespace catsfoot { template <typename T> struct verified<set<slow_set<T> > > : public std::true_type { }; }
To test this claim we now write a test program. We just make a small program that defines the data set we want to use and call the test driver for the concepts we want to check. We could define very complex random term generators, but for now, we will just give a list of sample data.
#include <limits> int main() { using cf::list_data_generator; using cf::choose; // To get better error messages, we should first statically check that // we are testing a valid claim. cf::assert_concept(set<slow_set<int> >{}); auto generator = choose // The generator can generate either: // - sets of type slow_set<int> (list_data_generator<slow_set<int> >( {slow_set<int>{}, slow_set<int>({0, 1}), slow_set<int>({0, 0}), slow_set<int>({1, 0})}), // - or integers list_data_generator<int>({0, 1, 2, 3, -1, -2, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()})); // We now test claim "set<slow_set<int> >" against generator. bool ret = cf::test_all<set<slow_set<int> > >(generator); // In our case, the implementations of the set was complete, so // we want to verify that we did cover all conditions in the axioms. ret = ret && cf::check_unverified(); return ret?0:1; }
This is the simplified picture of what testing looks like. The tutorials will cover each point in detail.