Unity more preprocessor help (C++) [updated]

This topic is 4607 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

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]

Share on other sites
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) );}

Share on other sites
I've been looking at that, and I don't understand how it works.

Share on other sites
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);

Share on other sites
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.

Share on other sites
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 EndarI don't understand this bit:typename boost::enable_if, 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.

Share on other sites
Quote:
 Original post by EndarFruny:: 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').

Share on other sites
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]

Share on other sites
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?

beep

.... Uh, bump.

1. 1
2. 2
3. 3
Rutin
15
4. 4
5. 5

• 10
• 9
• 9
• 11
• 11
• Forum Statistics

• Total Topics
633689
• Total Posts
3013337
×