可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Generally, I would use boost::mpl::for_each<>()
to traverse a boost::mpl::vector
, but this requires a functor with a template function declared like the following:
template<typename T> void operator()(T&){T::staticCall();}
My problem with this is that I don't want the object T to be instantiated by for_each<>
. I don't need the T parameter in the operator()
at all. Is there a way to accomplish this, or an alternative to for_each<>
that doesn't pass an object of type T to the template function?
Optimally, I would like the operator() definition to look like this:
template<typename T> void operator()(){T::staticCall();}
And of course, I don't want T to be instantiated at all prior to the call. Any other tips/suggestions are also welcome.
回答1:
Interesting question! As far as I can tell, Boost.MPL does not seem to provide such an algorithm. However, writing your own should not be too difficult using iterators.
Here is a possible solution:
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/next_prior.hpp>
#include <boost/mpl/vector.hpp>
using namespace boost::mpl;
namespace detail {
template < typename Begin, typename End, typename F >
struct static_for_each
{
static void call( )
{
typedef typename Begin::type currentType;
F::template call< currentType >();
static_for_each< typename next< Begin >::type, End, F >::call();
}
};
template < typename End, typename F >
struct static_for_each< End, End, F >
{
static void call( )
{
}
};
} // namespace detail
template < typename Sequence, typename F >
void static_for_each( )
{
typedef typename begin< Sequence >::type begin;
typedef typename end< Sequence >::type end;
detail::static_for_each< begin, end, F >::call();
}
[The naming may not be very well chosen, but well...]
Here is how you would use this algorithm:
struct Foo
{
static void staticMemberFunction( )
{
std::cout << "Foo";
}
};
struct Bar
{
static void staticMemberFunction( )
{
std::cout << "Bar";
}
};
struct CallStaticMemberFunction
{
template < typename T >
static void call()
{
T::staticMemberFunction();
}
};
int main()
{
typedef vector< Foo, Bar > sequence;
static_for_each< sequence, CallStaticMemberFunction >(); // prints "FooBar"
}
回答2:
Just encountered the same situation and provided different solution to the problem which I would like to share. It's not as that obvious, but it uses an existing algorithm. The idea is to use pointers instead.
typedef boost::mpl::vector<type1*, type2*> container;
struct functor
{
template<typename T> void operator()(T*)
{
std::cout << "created " << typeid(T).name() << std::endl;
}
};
int main()
{
boost::mpl::for_each<container>(functor());
}
so here we get a null pointers, but we don't care as we are not going to use them.
As I stated before that is not obvious in the code and would probably require some additional comments, but it still solves the question without writing any additional code.
added
I think Diego Sevilla suggested something similar.
回答3:
Well, first of all, the static call in your code means that your object will exist. Before/after is meaningless in that regard. The only time, "I don't want T to be instantiated at all prior to the call," make sense is when T is a template. It's not, because it can't be. It is true that it is that line that causes the object to exist, but I am pretty sure it won't just exist there once the product is compiled.
Second of all, I don't believe that there's a current method to use for_each without instantiating. IMHO this is a bug in MPL caused by a questionable decision to use operator(). Won't say it's wrong since I know the developer and he's a lot smarter than I am, but it seems so from here now that you bring this up.
So, I think you're stuck having to remake a for_each that calls a templated function that doesn't require the parameter. I'm almost certain it is possible, but also equally certain it's not readily available as a premade component in MPL.
回答4:
Marcin, you're very right. I've been thinking this along and I don't see an easy solution for this. Even if you cannot write the empty operator()
, it would be at least possible to use a pointer, that doesn't need an actual object to exist. You have to roll your own implementation, it seems.
回答5:
Here is an alternative solution highly inspired from Luc Touraille's answer.
This version is done using Metafunction classes instead of functions which allows the static_for_each
to be called even outside of function scopes (useful if the job has to be totally done at compiletime so you have no unnecessary functions called at runtime).
Furthermore it gives more interaction thanks to the first
and last
typedefs, allowing to get information out of the loop if necessary, a little like the way a return
works for a function.
You can also access the previous iteration result within each iteration thanks to the second template parameter Previous
passed to the metafunction class F
.
Finally you can provide data to the loop process using the Initial
template parameter, it will be given as the value of the Previous
parameter of the first iteration.
# include <boost/mpl/begin_end.hpp>
# include <boost/mpl/next_prior.hpp>
# include <boost/mpl/apply.hpp>
namespace detail_static_for_each
{
// Loop
template<typename Begin, typename End, typename F, typename Previous>
struct static_for_each
{
private:
typedef typename Begin::type current_type;
public:
typedef typename boost::mpl::apply<F, current_type, Previous>::type first;
typedef typename static_for_each<typename boost::mpl::next<Begin>::type, End, F, first>::last last;
};
// End of loop
template<typename End, typename F, typename Last>
struct static_for_each<End, End, F, Last>
{
public:
typedef Last first;
typedef Last last;
};
} // namespace detail_static_for_each
// Public interface
template<typename Sequence, typename F, typename Initial = void>
struct static_for_each
{
private:
typedef typename boost::mpl::begin<Sequence>::type begin;
typedef typename boost::mpl::end<Sequence>::type end;
typedef typename detail_static_for_each::static_for_each<begin, end, F, Initial> loop;
public:
typedef typename loop::first first;
typedef typename loop::last last;
};
Here is a simple example that both gives and retrieves data :
# include <iostream>
# include <boost/type_traits/is_same.hpp>
# include <boost/mpl/if.hpp>
# include <boost/mpl/vector.hpp>
# include "static_for_each.hpp"
struct is_there_a_float
{
template<typename currentItem, typename PreviousIterationType>
struct apply
{
typedef typename boost::mpl::if_< PreviousIterationType,
PreviousIterationType,
boost::is_same<float, currentItem> >::type type;
};
};
struct test
{
typedef boost::mpl::vector< char, long, long, double, float, int, char > sequence;
typedef static_for_each<sequence, is_there_a_float, boost::false_type>::last found;
};
int main(void)
{
std::cout << std::boolalpha << test::found::value << std::endl;
return (0);
}
These features makes the use of static_for_each
more similar to the use of common runtime loops (while
, for
, BOOST_FOREACH ...) as you can interact more directly with the loop.
回答6:
I liked (up-voted) the solution with pointer and own defined *_for_each function. Here is an alternative using type wrapper for T if the goal is to avoid object creation till it is needed.
template<typename T>
struct Wrapper
{
typedef T type;
};
struct Functor
{
template<typename T> void operator()(T t)
{
T::type obj(1);
T::type::static_fuc();
}
};
struct T1
{
T1(int a) : m_a(a) { }
int m_a;
static inline void static_fuc() { }
};
struct T2
{
T2(int a) : m_a(a) { }
int m_a;
static inline void static_fuc() { }
};
void fun()
{
namespace mpl=boost::mpl;
typedef mpl::vector<Wrapper<T1>,Wrapper<T2> > t_vec;
mpl::for_each<t_vec>(Functor());
}