c++-gtk-utils
Classes | Functions
Cgu::DoIf Namespace Reference

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

Classes

class  RelatedTest
 Class for compile time testing of inheritance relationships. More...
 

Functions

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

Detailed Description

This namespace 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.

Function Documentation

◆ assert_not_const()

template<class T >
void Cgu::DoIf::assert_not_const ( T &  t)

This function provides a compile time assertion that the static type of the argument passed is not const. If it is, a compile time error will occur (with a message that "Cgu::DoIf::assert_not_const() failed", and the line at which this function is reported as instantiated will be the line at which the assertion failed).

For example 'Cgu::DoIf::assert_not_const(a)' will fail to compile unless the static type of 'a' is not const.

It generates no object code, and is therefore thread safe and does not throw.

Since 2.0.1

◆ assert_related_to_type() [1/2]

template<class Base , class Obj >
void Cgu::DoIf::assert_related_to_type ( const Base &  b,
const Obj &  o 
)

This function provides a compile time assertion that the static type of the second argument passed to it is related to (that is, either the same as or publicly derived from) the static type of the first argument. If it is not, a compile time error will occur (with a message that "Cgu::DoIf::assert_related_to_type() failed", and the line at which this function is reported as instantiated will be the line at which the assertion failed).

For example "Cgu::DoIf::assert_related_to_type(a, b)" will fail to compile unless the static type of 'b' is the same as or publicly derived from the static type of 'a'. In making this test, constness is discarded and not taken into account.

See assert_related_types() for a test where the ordering of the arguments is not significant.

This function can be viewed as doing the opposite of Cgu::DoIf::mem_fun() and Cgu::DoIf::fun(): it causes a compilation error where the test is not met, rather than doing nothing. However, unlike those functions, as mentioned above assert_related_to_type() discards constness in making the test.

It generates no object code, and is therefore thread safe and does not throw.

◆ assert_related_to_type() [2/2]

template<class Base , class Obj >
void Cgu::DoIf::assert_related_to_type ( const Obj &  o)

This function provides a compile time assertion that the static type of the argument passed to it is related to (that is, either the same as or publicly derived from) the type given as the explicit template parameter to the function call. If it is not, a compile time error will occur (with a message that "Cgu::DoIf::assert_related_to_type() failed", and the line at which this function is reported as instantiated will be the line at which the assertion failed).

For example "Cgu::DoIf::assert_related_to_type<A>(x)" will fail to compile unless the static type of 'x' is the same as or publicly derived from type A. In making this test, constness is discarded and not taken into account.

This function can be viewed as doing the opposite of Cgu::DoIf::mem_fun() and Cgu::DoIf::fun(): it causes a compilation error where the test is not met, rather than doing nothing. However, unlike those functions, as mentioned above assert_related_to_type() discards constness in making the test.

It generates no object code, and is therefore thread safe and does not throw.

◆ assert_related_types()

template<class T1 , class T2 >
void Cgu::DoIf::assert_related_types ( const T1 &  t1,
const T2 &  t2 
)

This function provides a compile time assertion that the static type of the arguments passed are related to each other (that is, either they are the same or one is publicly derived from the other). If they are not, a compile time error will occur (with a message that "Cgu::DoIf::assert_related_types() failed", and the line at which this function is reported as instantiated will be the line at which the assertion failed).

For example "Cgu::DoIf::assert_related_types(a, b)" will fail to compile unless the static types of 'a' and 'b' are the same or one of the static types is publicly derived from the other. In making this test, constness is discarded and not taken into account: types can be related even if one is const and the other is not.

See assert_related_to_type() for a test where the ordering of the arguments is significant, so that which of them must be the base type can be stated.

This function can be viewed as doing the opposite of Cgu::DoIf::mem_fun() and Cgu::DoIf::fun(): it causes a compilation error where the test is not met, rather than doing nothing. However, unlike those functions, as mentioned above assert_related_types() discards constness in making the test.

It generates no object code, and is therefore thread safe and does not throw.

◆ assert_same_type() [1/2]

template<class T1 , class T2 >
void Cgu::DoIf::assert_same_type ( const T1 &  t1,
const T2 &  t2 
)

This function provides a compile time assertion that the static type of the first argument passed to it is the same as the static type of the second argument. If it is not, a compile time error will occur (with a message that "Cgu::DoIf::assert_same_type() failed", and the line at which this function is reported as instantiated will be the line at which the assertion failed).

For example "Cgu::DoIf::assert_same_type(a, b)" will fail to compile unless the static types of 'a' and 'b' are the same. In making this test, constness is discarded and not taken into account: types can be the same even if one is const and the other is not.

It generates no object code, and is therefore thread safe and does not throw.

◆ assert_same_type() [2/2]

template<class T1 , class T2 >
void Cgu::DoIf::assert_same_type ( const T2 &  t2)

This function provides a compile time assertion that the static type of the argument passed to it is the same as the type given as the explicit template parameter to the function call. If it is not, a compile time error will occur (with a message that "Cgu::DoIf::assert_same_type() failed", and the line at which this function is reported as instantiated will be the line at which the assertion failed).

For example "Cgu::DoIf::assert_same_type<A>(x)" will fail to compile unless the static type of 'x' is type A. In making this test, constness is discarded and not taken into account: types can be the same even if one is const and the other is not.

It generates no object code, and is therefore thread safe and does not throw.

Earlier versions of this function had a bug. A const type could not be specified as the explicit template argument (Cgu::DoIf::assert_same_type<const A>(x) would not work). This was fixed in version 2.0.1.

◆ fun() [1/2]

template<class Base , class Obj , class Ret , class... Params, class... Args>
Ret Cgu::DoIf::fun ( Obj &  obj,
Ret(*)(Base *, Params...)  func,
Args &&...  args 
)

This function overload conditionally executes the function passed as the second argument if the object passed as the first argument relates to (ie is an instance of or derived from) the type to which the first argument of the function to be conditionally executed is a pointer, otherwise it does nothing. This function is thread safe if the referenced function to be called is thread safe. It does not throw unless the referenced function throws or (if its arguments are not built-in types nor reference arguments) the copy constructor of one of the referenced function's arguments throws, or (if no call is made) the return type's default constructor throws.

Returns
The result of the function call, or if no call is made, an empty object obtained by calling the return type's default constructor.

◆ fun() [2/2]

template<class Base , class Obj , class... Params, class... Args>
void Cgu::DoIf::fun ( Obj &  obj,
void(*)(Base *, Params...)  func,
Args &&...  args 
)

This function overload conditionally executes the function passed as the second argument if the object passed as the first argument relates to (ie is an instance of or derived from) the type to which the first argument of the function to be conditionally executed is a pointer, otherwise it does nothing. This function is thread safe if the referenced function to be called is thread safe. It does not throw unless the referenced function throws or (if its arguments are not built-in types nor reference arguments) the copy constructor of one of the referenced function's arguments throws.

◆ mem_fun() [1/4]

template<class Base , class Obj , class Ret , class... Params, class... Args>
Ret Cgu::DoIf::mem_fun ( const Obj &  obj,
Ret(Base::*)(Params...) const  func,
Args &&...  args 
)

This function overload conditionally executes the member function passed as the second argument if the class object passed as the first argument has it as a member (say by derivation), otherwise it does nothing. This function is thread safe if the referenced member function is thread safe. It does not throw unless the referenced member function throws or (if its arguments are not built-in types nor reference arguments) the copy constructor of one of the member function's arguments throws, or (if no call is made) the return type's default constructor throws.

Returns
The result of the function call, or if no call is made, an empty object obtained by calling the return type's default constructor.

◆ mem_fun() [2/4]

template<class Base , class Obj , class... Params, class... Args>
void Cgu::DoIf::mem_fun ( const Obj &  obj,
void(Base::*)(Params...) const  func,
Args &&...  args 
)

This function overload conditionally executes the member function passed as the second argument if the class object passed as the first argument has it as a member (say by derivation), otherwise it does nothing. This function is thread safe if the referenced member function is thread safe. It does not throw unless the referenced member function throws or (if its arguments are not built-in types nor reference arguments) the copy constructor of one of the member function's arguments throws.

◆ mem_fun() [3/4]

template<class Base , class Obj , class Ret , class... Params, class... Args>
Ret Cgu::DoIf::mem_fun ( Obj &  obj,
Ret(Base::*)(Params...)  func,
Args &&...  args 
)

This function overload conditionally executes the member function passed as the second argument if the class object passed as the first argument has it as a member (say by derivation), otherwise it does nothing. This function is thread safe if the referenced member function is thread safe. It does not throw unless the referenced member function throws or (if its arguments are not built-in types nor reference arguments) the copy constructor of one of the member function's arguments throws, or (if no call is made) the return type's default constructor throws.

Returns
The result of the function call, or if no call is made, an empty object obtained by calling the return type's default constructor.

◆ mem_fun() [4/4]

template<class Base , class Obj , class... Params, class... Args>
void Cgu::DoIf::mem_fun ( Obj &  obj,
void(Base::*)(Params...)  func,
Args &&...  args 
)

This function overload conditionally executes the member function passed as the second argument if the class object passed as the first argument has it as a member (say by derivation), otherwise it does nothing. This function is thread safe if the referenced member function is thread safe. It does not throw unless the referenced member function throws or (if its arguments are not built-in types nor reference arguments) the copy constructor of one of the member function's arguments throws.

Cgu::EmitterArg
A class to execute callbacks connected to it, with provision for automatic disconnection.
Definition: emitter.h:307
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