more preprocessor help (C++) [updated]

Started by
11 comments, last by Fruny 17 years, 11 months ago
Okay, I'm back asking more questions on this topic, so, if you're new, reading through this whole thing might help if you ever need to perform some preprocessor magic. :D This is the start of the current batch of posts. Okay, I'm back with more preprocessor questions, this time hopefully it will be quick and easy. I'm using boost and a bunch of boost preprocessor macros to create different definitions of a template class. My current problem is that at one point I have this code snippet:

/* this evaluates to true iff 'R' is 'void' */
/* http://www.boost.org/doc/html/boost_typetraits.html */
if( m_isVoid.value ) /* if the return is void, don't set the return value */ m_function( WRITE_ARR_ELEMENTS(N, input) ); /* Call the function and get the return value */ else output = m_function( WRITE_ARR_ELEMENTS(N, input) ); Where m_function is a boost::functionN object, and output is a boost::variant which contains pretty much every primitive type, a pointer to every primitive type and also contains the same for std::string. The macro will expand like so: WRITE_ARR_ELEMENTS(2, array) will expand to "boost::get<T0>(array[0]) , boost::get<T1>(array[1])" So, my current problem is that the assignment causes the compiler to have a fit when 'void' is the return value because void isn't in the boost::variant template list for obvious reasons. So, I'm looking for a way to identify at preprocessor time whether a template parameter is 'void' or not, which I'm fairly sure is impossible as types don't matter until the code is actually compiled. [Edited by - Endar on May 2, 2006 7:29:41 PM]
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Advertisement
I don't think you need to check for void at preprocessor time; compile time works just fine (note: currently you do the check at *run* time). Write two templated overloads for the function, and use boost::enable_if to select the correct overload. Something like this:

typename boost::enable_if<IsVoid, void>::typecall(input_t& input, output_t& output){  m_function( WRITE_ARR_ELEMENTS(N, input) );}typename boost::disable_if<IsVoid, void>::typecall(input_t& input, output_t& output){  output = m_function( WRITE_ARR_ELEMENTS(N, input) );}
I've been looking at that, and I don't understand how it works.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
It works on the SFINAE principle (substitution failure is not an error), which means that if parameter substitution generates a nonsensical template then that template is just ignored during overload resolution instead of generating an error.

In practice, if IsVoid is false, the compiler will not see the first function, and if false, it will not see the second.

Of course, in the present case, you could just write:

template<bool b> voidcall(input_t& input, output_t& output){  m_function( WRITE_ARR_ELEMENTS(N, input) );}template<> voidcall<false>(input_t& input, output_t& output){  output = m_function( WRITE_ARR_ELEMENTS(N, input) );}call<IsVoid>(in, out);
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Okay, well, help. This is a test class so I can get the concept working before plugging it into my real code (which incidently is also test code, so I can get that working, and so on).

template <class T>class A{public:	/* is_void only inherits from 'false_type' iff the template param */	/* is not 'void' */	template <class A>	struct is_void	:	public boost::false_type{};	/* is_void only inherits from 'true_type' iff the template param */	/* is 'void' */	template <>	struct is_void<void>	:	public boost::true_type{};	typename boost::enable_if<is_void<T>, void>::type	void func( )	{		printf("\n Enabled!! \n");	}	typename boost::disable_if<is_void<T>, void>::type	void func( )	{		printf("\n Disabled!! \n");	}};// class A


I don't understand this bit:
typename boost::enable_if<is_void<T>, void>::typevoid func( )


I don't understand how this is proper syntax. How does the "typename boost::stuff" supposed to be valid? I mean, it's obviously not a return type. The function doesn't look like a template function.

I'm kind of in the dark.

Fruny:: I would go with your code except for the fact that when I put it into it's final destination in my project, I'll be calling the 'call' function from outside the class, and I don't want to have to remember to always call the 'call' function with a template param like that.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
First off, as I suddenly realised, enable_if doesn't work very well in this case, because SFINAE is only invoked on function templates, not on any old overload. So, my example was misleading. Anyway, in this particular case, Fruny's advice should probably be followed. You don't need to explicitly write the template parameter everywhere, just write a trivial wrapper:

void call(input_t& input, output_t& output){  call_helper<is_void::value>(input, output);}private: template<bool b> void call_helper(input_t& input, output_t& output){  m_function( WRITE_ARR_ELEMENTS(N, input) );}template<> void call_helper<false>(input_t& input, output_t& output){  output = m_function( WRITE_ARR_ELEMENTS(N, input) );}call(in, out);



But, for reference:

Quote:Original post by Endar
I don't understand this bit:
typename boost::enable_if<is_void<T>, void>::typevoid func( )


You have one void too many: the typename .. ::type part *is* the return value - a typedef for the second template parameter (which defaults to void). Basically it is just a clever trick to explicitly invoke SFINAE; the actual implementation of enable_if is very simple (straight from the docs:
template <bool B, class T = void>struct enable_if_c {  typedef T type;};template <class T>struct enable_if_c<false, T> {};template <class Cond, class T = void>struct enable_if : public enable_if_c<Cond::value, T> {};


Thus, if you have this:

template<typename T>
typename boost::enable_if<is_void<T> >::type func( );

then, if the condition evaluates to true, the nested typedef type exists and the declaration is well-formed and will thus be considered (and selected) in overload resolution. If not, there's no typedef and the declaration invalid - but SFINAE says that this will not result in error, the function will just be ignored.
Quote:Original post by Endar
Fruny:: I would go with your code except for the fact that when I put it into it's final destination in my project, I'll be calling the 'call' function from outside the class, and I don't want to have to remember to always call the 'call' function with a template param like that.


You can probably solve that with another layer of indirection (i.e. another function call). The function calls will probably be optimized away (think 'inline').
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Okay, I think it's done.

I'll just post my test code here for anyone else who happens along this thread.


template <class T>class A{public:	/* is_void only inherits from 'false_type' iff the template param */	/* is not 'void' */	template <class A>	struct is_void	:	public boost::false_type{};	/* is_void only inherits from 'true_type' iff the template param */	/* is 'void' */	template <>	struct is_void<void>	:	public boost::true_type{};	void func()	{		func_helper< is_void<T>::value >();	}	template <bool b>	void func_helper( )	{		printf("\n Enabled!! \n");	}	template <>	void func_helper<false>( )	{		printf("\n Disabled!! \n");	}};// class A


Thanks guys, I should be okay now, but who knows? [smile]
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
Okay, well, actually as long as I'm here, I might as well ask my last question, else I'll have to explain the whole thing again in another thread.

The non-member functions seem to work pretty well, I'm just working on getting member functions to work.

If you don't know how boost::function works, if you're calling a member function you need a pointer to the object as the first function argument like so:

struct A{// stuff here};int main(){	boost::function< void (A*) > f;}


Currently I have my constuctor made up with boost preprocessor macros to expand like this:
CFunctionWrapper_1 ( R (*f)(  T0 ) )

Where R is the template param for the return value and T0 is the first function argument. This is obviously for a function with only 1 argument.

My main problem is that I don't want to have to add another type to the template parameter of the boost::variant type that I'm using every time I want to use a new object or another function.

Anyone have a creative idea? Or would the best idea be to just forget member functions and stick with non-member functions?
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper
beep



.... Uh, bump.
[size="2"][size=2]Mort, Duke of Sto Helit: NON TIMETIS MESSOR -- Don't Fear The Reaper

This topic is closed to new replies.

Advertisement