是否可以在运行时迭代 mpl::vector 而不实例化向量中的类型?

Posted

技术标签:

【中文标题】是否可以在运行时迭代 mpl::vector 而不实例化向量中的类型?【英文标题】:Is it possible to iterate an mpl::vector at run time without instantiating the types in the vector? 【发布时间】:2010-10-29 15:38:07 【问题描述】:

一般来说,我会使用boost::mpl::for_each<>() 来遍历boost::mpl::vector,但这需要一个仿函数,其模板函数声明如下:

template<typename T> void operator()(T&)T::staticCall();

我的问题是我不希望对象 T 被 for_each<> 实例化。我根本不需要operator() 中的 T 参数。有没有办法做到这一点,或者for_each<> 的替代方法不会将 T 类型的对象传递给模板函数?

理想情况下,我希望 operator() 定义如下所示:

template<typename T> void operator()()T::staticCall();

当然,我不希望 T 在调用之前被实例化。也欢迎任何其他提示/建议。

【问题讨论】:

【参考方案1】:

刚刚遇到同样的情况,针对问题提供了不同的解决方案,我想分享一下。它并不那么明显,但它使用了现有的算法。这个想法是改用指针。

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());

所以这里我们得到一个空指针,但我们不在乎,因为我们不会使用它们。

正如我之前所说,这在代码中并不明显,可能需要一些额外的 cmets,但它仍然可以解决问题,而无需编写任何额外的代码。

添加

我认为 Diego Sevilla 提出了类似的建议。

【讨论】:

这很聪明。您可以使用mpl::transform 动态定义“指针”版本:boost::mpl::for_each&lt;boost::mpl::transform&lt;MyTypes, boost::add_pointer&lt;boost::mpl::_1&gt; &gt;::type&gt;(Functor()); @kizzx2 你甚至可以不提供指针,而是提供一个包含该类型的空类型。【参考方案2】:

有趣的问题!据我所知,Boost.MPL 似乎没有提供这样的算法。但是,使用迭代器编写自己的代码应该不会太难。

这是一个可能的解决方案:

#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();

[命名可能选得不是很好,但是很好……]

以下是您将如何使用此算法:

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"

【讨论】:

这里有非常有用的指导。谢谢。【参考方案3】:

首先,代码中的静态调用意味着您的对象将存在。在这方面,之前/之后是没有意义的。唯一一次,“我不希望 T 在调用之前被实例化”是有意义的,当 T 是一个模板时。不是,因为不可能。确实是那一行导致对象存在,但我很确定一旦产品被编译,它就不会只存在于那里。

其次,我不相信有一种当前的方法可以在不实例化的情况下使用 for_each。恕我直言,这是 MPL 中的一个错误,由使用 operator() 的可疑决定引起。不会说这是错误的,因为我认识开发人员,而且他比我聪明得多,但是现在你提出这个问题似乎是这样。

所以,我认为您不得不重新制作一个调用不需要参数的模板化函数的 for_each。我几乎可以肯定它是可能的,但也同样可以肯定它在 MPL 中作为预制组件并不容易获得。

【讨论】:

【参考方案4】:

Marcin,你说的很对。我一直在考虑这个问题,但我没有看到一个简单的解决方案。即使你不能写空的@​​987654321@,至少可以使用一个不需要实际对象存在的指针。看来你必须推出自己的实现。

【讨论】:

我认为没有“简单”的解决方案,因为 easy 解决方案已经存在:使用mpl::beginmpl::end。基本上你需要自己遍历它。就像你必须为迭代器写很长的for(std::vector&lt;int&gt;::iterator it = v.begin() ... 东西一样。您可以编写一个高阶函数来简化它,但最终您仍然需要像@Luc Touraille 建议的那样遍历它。【参考方案5】:

这是一个受Luc Touraille's answer启发的替代解决方案。

这个版本是使用Metafunction classes而不是函数完成的.

此外,由于firstlast 类型定义,它提供了更多交互,允许在必要时从循环中获取信息,有点像return 用于函数的方式。

由于传递给元函数类F的第二个模板参数Previous,您还可以在每次迭代中访问上一次迭代结果。

最后你可以使用Initial模板参数向循环过程提供数据,它将作为第一次迭代的Previous参数的值给出。

# 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;
;

这是一个简单的例子,既提供数据又检索数据:

# 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);                                                                                                                                                                                                        

这些功能使static_for_each 的使用更类似于使用常见的运行时循环(whilefor、BOOST_FOREACH ...),因为您可以更直接地与循环交互。

【讨论】:

据我所知,您只是重新实现了mpl::fold。 mpl::for_each 的主要特点是它在运行时运行(好吧,迭代是在编译时执行的,但它调用运行时操作),我假设 OP 想要这种行为。否则,他会使用 MPL 提供的众多编译时算法之一。 @LucTouraille 我同意你的看法。这个答案更多的是作为奖励信息而不是直接答案。【参考方案6】:

我喜欢(赞成)带有指针和自己定义的 *_for_each 函数的解决方案。如果目标是在需要之前避免创建对象,那么这是使用 T 类型包装器的替代方法。

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());

【讨论】:

以上是关于是否可以在运行时迭代 mpl::vector 而不实例化向量中的类型?的主要内容,如果未能解决你的问题,请参考以下文章

从 bigquery 中获取数据而不重复

如何在运行时检查权限而不抛出 SecurityException?

Python:如何在迭代列表时从列表中删除元素而不跳过未来的迭代

是否可以编译 Symfony2assetic:dump 并部署它而不是在服务器上运行它?

在 R 中运行 foreach 而不返回任何值

多次调用一个函数而不等待它完成