c++-gtk-utils
Classes | Namespaces | Functions
do_if.h File Reference

This file provides utility functions for conditional compilation. More...

#include <utility>
#include <type_traits>
#include <c++-gtk-utils/cgu_config.h>

Go to the source code of this file.

Classes

class  Cgu::DoIf::RelatedTest< Base, Obj >
 Class for compile time testing of inheritance relationships. More...
 

Namespaces

 Cgu::DoIf
 This namespace provides utility functions for conditional compilation.
 
 Cgu
 

Functions

template<class Base , class Obj >
void Cgu::DoIf::assert_related_to_type (const Base &b, const Obj &o) noexcept
 
template<class Base , class Obj >
void Cgu::DoIf::assert_related_to_type (const Obj &o) noexcept
 
template<class T1 , class T2 >
void Cgu::DoIf::assert_same_type (const T1 &t1, const T2 &t2) noexcept
 
template<class T1 , class T2 >
void Cgu::DoIf::assert_same_type (const T2 &t2) noexcept
 
template<class T1 , class T2 >
void Cgu::DoIf::assert_related_types (const T1 &t1, const T2 &t2) noexcept
 
template<class T >
void Cgu::DoIf::assert_not_const (T &t) noexcept
 
template<class Base , class Obj , class Ret , class... Params, class... Args>
Ret Cgu::DoIf::mem_fun (Obj &obj, Ret(Base::*func)(Params...), Args &&... args)
 
template<class Base , class Obj , class... Params, class... Args>
void Cgu::DoIf::mem_fun (Obj &obj, void(Base::*func)(Params...), Args &&... args)
 
template<class Base , class Obj , class Ret , class... Params, class... Args>
Ret Cgu::DoIf::mem_fun (const Obj &obj, Ret(Base::*func)(Params...) const, Args &&... args)
 
template<class Base , class Obj , class... Params, class... Args>
void Cgu::DoIf::mem_fun (const Obj &obj, void(Base::*func)(Params...) const, Args &&... args)
 
template<class Base , class Obj , class Ret , class... Params, class... Args>
Ret Cgu::DoIf::fun (Obj &obj, Ret(*func)(Base *, Params...), Args &&... args)
 
template<class Base , class Obj , class... Params, class... Args>
void Cgu::DoIf::fun (Obj &obj, void(*func)(Base *, Params...), Args &&... args)
 

Detailed Description

This file provides utility functions for conditional compilation.

#include <c++-gtk-utils/do_if.h>

The DoIf namespace of the library provides a small subset of template meta-programming techniques likely to be most relevant to GUI programming (if something more substantial is required, something like boost or loki should be used).

For conditional compilation, the library provides the Cgu::DoIf::mem_fun() utility functions which will conditionally compile a call to a non-static class member function if, and only if, the target object has that function as a member. If it does not have the member function as a member, the call will just do nothing rather than generate a compiler error, as it will also if the target object is const and the member function to be called is non-const. In addition Cgu::DoIf::fun() will conditionally compile a call to an ordinary function (or static member function) if, and only if, the target object is convertible to the type (say, a base type) held by pointer as the first argument of that function.

They therefore provide compile-time inheritance checking based on the static type of the object passed to them. They save dynamic casting (and therefore run time overhead and the requirement for target types to have a virtual method) in templated functions when the static type is all that is necessary to enable type checking to be carried out.

This is not just a template curiosity, but enables more generic programming practices when, say, passing mixins to templated functions. A templated function can be passed an object as a template type, and that function can then conditionally call member functions of objects passed to it. If the object has a particular function as a member, Cgu::DoIf::mem_fun() will call the function, and if not it will do nothing without causing a compilation error. It can be particularly useful for templated set-up or tear-down functions intended to take a wide variety of different objects with differing features.

As mentioned above, Cgu::DoIf::mem_fun() and Cgu::DoIf::fun() will do nothing if an attempt is made to execute a non-const function on a const object, as well as if the object type does not match the function or member function sought to be invoked. If a compilation failure is wanted in a case of const mismatch instead of doing nothing, from version 2.0.1 the static assertion Cgu::DoIf::assert_not_const() can be invoked before the call to a non-const function is made by Cgu::DoIf::mem_fun() or Cgu::DoIf::fun().

Non-static member functions

The first argument to be passed to Cgu::DoIf::mem_fun() is the target object to be tested and the second argument is the non-static member function which is to be called with respect to that object if it has the function as a member. Any number of further arguments can be passed, representing the arguments to be provided to that member function. These further arguments may be reference arguments (const or non-const), value arguments or r-values, and for automatic pass-through they are passed by r-value reference and std::forward(), so no overhead should be created if value or r-value arguments are passed via Cgu::DoIf::mem_fun().

As an example, take a class which uses multiple inheritance to inherit from Cgu::EmitterArg as a mixin (normally a class would have an Emitter object as a member, but it can be inherited from). A container of objects could have the emit() method called on them in a case where they do inherit from EmitterArg, where the code does nothing if they do not. For example:

class Calc {
public:
void do_calc(void);
};
class CalcEmit: public Calc,
public Cgu::EmitterArg<int> {
};
... some code blocks elsewhere ...
template <class T>
void dispatch_calcs(const std::vector<T>& v) {
int count;
typename std::vector<T>::const_iterator iter;
for (count = 0, iter = v.begin();
iter != v.end();
++count, ++iter) {
iter->do_calc();
Cgu::DoIf::mem_fun(*iter, // object to be tested
&Cgu::EmitterArg<int>::emit, // member function to be conditionally called
count); // argument
}
}

Ordinary functions and static member functions

The use of Cgu::DoIf::fun() with normal functions and static member functions is similar to the use of mem_fun() with non-static member functions, except that the first argument of the function to be conditionally called must be a pointer to a type to which the target object can prospectively be converted. The first argument to be passed to Cgu::DoIf::fun() is the target object to be tested and the second argument is the function which is to be conditionally called if the conversion can be accomplished. If the function to be conditionally called is so called, the target object will be passed by pointer to it. Any number of further arguments can be passed to Cgu::DoIf::fun(), representing additional arguments to be provided to the function to be conditionally called, which may be reference arguments, value arguments or r-value reference arguments (and automatic pass-through is provided, as in the case of mem_fun()).

The use of Cgu::DoIf::fun() with ordinary functions can be useful for batching a number of calls to be made to a test object. For example:

class Calc {
public:
void do_calc();
};
class CalcEmit: public Calc,
public Cgu::EmitterArg<int> {
public:
void clean_up(int);
};
... some code blocks elsewhere ...
void emit_on_calcs(CalcEmit* c, int count) {
c->emit(count);
c->clean_up(count);
}
template <class T>
void dispatch_calcs(const std::vector<T>& v) {
int count;
typename std::vector<T>::const_iterator iter;
for (count = 0, iter = v.begin();
iter != v.end();
++count, ++iter) {
iter->do_calc();
Cgu::DoIf::fun(*iter, // object to be tested against first argument of function to be conditionally called
&emit_on_calcs, // function to be conditionally called
count); // second argument of function to be conditionally called
}
}

Return values

If the function to be called returns something other than void and the function to be conditionally called is not called, then mem_fun() and fun() pass back a dummy object of the return type using that type's default constructor. The returned object has no meaning beyond that in such a case, so it should not be relied on as holding anything other than its default value on default construction in a case of failure. This in turn means that any return value, if not a built-in type, must have a default constructor.

Overloaded functions

Where a referenced member function or ordinary function is overloaded, this will cause difficulties in template type deduction when mem_fun() or fun() is called, requiring explicit disambiguation. For example the following will fail to compile unless explicitly disambiguated:

class A {
public:
void do_it(int i);
void do_it(int i, int j);
void do_it(double d);
};
int func(A* a, int i);
int func(A* a, double d);
template <class T>
void do_it_if(T& t) {
int i = 1, j = 2;
double d = 10.0;
Cgu::DoIf::mem_fun(t, &A::do_it, i); // won't compile
Cgu::DoIf::mem_fun(t, &A::do_it, i, j); // won't compile
Cgu::DoIf::mem_fun(t, &A::do_it, d); // won't compile
Cgu::DoIf::fun(t, func, i); // won't compile
Cgu::DoIf::fun(t, func, d); // won't compile
Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(int)>(&A::do_it), i); // OK
Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(int, int)>(&A::do_it), i, j); // OK
Cgu::DoIf::mem_fun(t, static_cast<void (A::*)(double)>(&A::do_it), d); // OK
Cgu::DoIf::fun(t, static_cast<int (*)(A*, int)>(func), i); // OK
Cgu::DoIf::fun(t, static_cast<int (*)(A*, double)>(func), d); // OK
}

Static assertion

This library also provides the Cgu::DoIf::assert_related_to_type(), Cgu::DoIf::assert_related_types() and Cgu::DoIf::assert_same_type() static type-checking assertions. If the matters asserted are not true, a compile time error in the line where the functions are instantiated will occur.

These static assertion functions can be viewed as doing the opposite of Cgu::DoIf::mem_fun() and Cgu::DoIf::fun(). The mem_fun() and fun() functions will make a conditional call, and do nothing if the test condition is not met without a compile time or run time error, in order to provide compile time polymorphism. On the other hand, assert_related_to_type(), assert_related_types() and assert_same_type() cause an explicit compilation error where the test condition is not met at an ascertainable point in the code whilst avoiding a slew of incomprehensible error messages from the compiler. They enable compile time checking of type related invariants.

In addition, from version 2.0.1, a Cgu::DoIf::assert_not_const() static assertion is available.

The static assertions add nothing to the compiled object code. They either succeed or fail at compile time.

Cgu::EmitterArg
A class to execute callbacks connected to it, with provision for automatic disconnection.
Definition: emitter.h:496
Cgu::DoIf::mem_fun
Ret mem_fun(Obj &obj, Ret(Base::*func)(Params...), Args &&... args)
Definition: do_if.h:849
Cgu::DoIf::fun
Ret fun(Obj &obj, Ret(*func)(Base *, Params...), Args &&... args)
Definition: do_if.h:942