【问题标题】:boost::join and boost::transformedboost::join 和 boost::transformed
【发布时间】:2014-05-09 06:10:55
【问题描述】:

我想使用 boost 将字符串与 "," 连接起来。但是我有一个 shared_ptr 的向量,我需要将其转换为字符串。

(愚蠢的做法是使用 boost::transform 将我的共享指针转换为字符串向量)。 但我想做这样的事情。

注意:我正在使用 VS2012(以及用于 Linux 构建的 gcc 4.5.3)和 boost 1.48.0

C++ 代码。

std::string A::returnConcatString() const
{
    return boost::algorithm::join( sharedPtrList() | boost::adaptors::transformed( 
                                       [](B::ConstPtr obj )->std::string
                                       { 
                                           return ( boost::format( "%s:%s" ) % obj->string1() % obj->string2() ).str(); 
                                       } ),
                                  ",");
}

(顺便说一句,如果我使用匿名函数而不是使用 string2() 会发生什么?)

HPP 代码。

class A
{
    public:
    /***code***/
    vector< B::ConstPtr > sharedPtrList() const;
    std::string returnConcatString() const;
}

class B
{ 
  public:
  typedef boost::shared_ptr<const B> ConstPtr;
  std::string string1() const;
  std::string string2() const;
}

有没有更好的方法来做到这一点?谁能帮我理解我的问题?

另外,我发现了这个链接http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3594.html(这正是我想要模仿的)

当我编译这段代码时,我得到一个错误。

C:\temp\tools\Windows\Boost\1.48.0\boost/utility/result_of.hpp(79): error C2903: 'result' : symbol is neither a class template nor a function template
C:\temp\tools\Windows\Boost\1.48.0\boost/utility/result_of.hpp(87) : see reference to class template instantiation 'boost::detail::result_of_nested_result<F,FArgs>' being compiled
with
[
    F=balh::`anonymous-namespace'::<lambda0>,
    FArgs=balh::`anonymous-namespace'::<lambda0> (const boost::shared_ptr<const balh::B> &)
]
C:\temp\tools\Windows\Boost\1.48.0\boost/utility/detail/result_of_iterate.hpp(33) : see reference to class template instantiation 'boost::detail::tr1_result_of_impl<F,FArgs,HasResultType>' being compiled
with
[
    F=balh::`anonymous-namespace'::<lambda0>,
    FArgs=balh::`anonymous-namespace'::<lambda0> (const boost::shared_ptr<const balh::B> &),
    HasResultType=false
]
C:\temp\tools\Windows\Boost\1.48.0\boost/utility/detail/result_of_iterate.hpp(81) : see reference to class template instantiation 'boost::tr1_result_of<F>' being compiled
with
[
    F=balh::`anonymous-namespace'::<lambda0> (const boost::shared_ptr<const balh::B> &)
]
C:\temp\tools\Windows\Boost\1.48.0\boost/mpl/eval_if.hpp(41) : see reference to class template instantiation 'boost::result_of<F>' being compiled

编辑:感谢 [utnapistim] 的想法。我现在正在使用这个。但我真的很想写一个单行:)。

我最终这样做了。

std::string ret;
std::for_each ( array.begin(), array.end(), [&ret](B::ConstPtr firmware) 
                                                        { 
                                                            ret.append( ( boost::format( "[%s:%s]" ) % firmwareTypeToString( firmware->firmwareType() ) % firmware->version() ).str() );
                                                        }
                  );

【问题讨论】:

  • 做一些可读且实际有效的事情并不是“愚蠢的做法”
  • 我同意(我通常这样做),但如果可以避免的话,我不想创建另一个向量(在这种情况下)(特别是因为我不打算将新向量用于除了 boost::join)。
  • 喂?为什么需要创建向量?流媒体怎么了?和循环(for_each,如果你坚持的话?)。您将立即获得大量性能,因为您仍在创建大量中间字符串,所有这些都需要动态分配。
  • 我现在最终使用了 for_each。将尝试使用流式传输。
  • 为了您的利益(和我的好奇心),我添加了一些基准to my answer

标签: c++ boost


【解决方案1】:

我会这样写:

std::string returnConcatString() const {
    std::ostringstream os;
    for(auto& b : sharedPtrList)
        os << b->string1() << ':' << b->string2() << ',';

    auto s = os.str();
    if (!s.empty())
        s.resize(s.size()-1);
    return s; // moves
}

令人惊讶的是,编译时间更好,运行时将比您的代码实现的要好很多。

看Live On Coliru

如果您真的关心性能,我建议您使用 Boost Karma:

std::string returnConcatString() const {
    if (sharedPtrList.empty())
        return {};

    std::string result;
    result.reserve(20 * sharedPtrList.size()); // average length preallocate?

    auto out = back_inserter(result);

    namespace k =  boost::spirit::karma;
    for(auto& b : sharedPtrList)
        k::generate(out, k::string << ':' << k::string << ',', b->string1(), b->string2());

    result.resize(result.size()-1);
    return result; // moves
}

也看到Live On Coliru

性能比较:

如您所见,stringstream 方法只对小型集合不利。

原始数据和benchmark code

【讨论】:

  • 为了您的利益(和我的好奇心),我添加了一些基准
  • 借此机会与 nonius 一起玩:source code 和结果graphs 和统计数据:paste.ubuntu.com/7418821
  • 嗯.. 真的很有趣。猜测字符串流是“简单”的方法。
【解决方案2】:

我用这个:

/* 
 * File:   ostream_join_iterator.hpp
 * Author: utnapistim :)
 *
 * Created on July 13, 2013, 1:14 AM
 */

#ifndef OSTREAM_JOIN_ITERATOR_HPP
#define OSTREAM_JOIN_ITERATOR_HPP

/**
 *  @brief  Implements join functionality directing into an ostream
 *
 *  This class provides an iterator for writing into an ostream. 
 *  The type T is the only type written by this iterator and there must 
 *  be an operator << (T) defined.
 * 
 *  @remarks This class implements functionality similar to
 *      std::ostream_iterator except it does not print a separator after the
 *      last element.
 *
 *  @param  Tp  The type to write to the ostream.
 *  @param  CharT  The ostream char_type.
 *  @param  Traits  The ostream char_traits.
 */
template<typename T, typename C=char, typename Tr=std::char_traits<C> >
class ostream_join_iterator
: public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
    typedef C  char_type;
    typedef Tr traits_type;
    typedef std::basic_string<C, Tr>  splitter_type;
    typedef std::basic_ostream<C, Tr> ostream_type;

private:
    ostream_type        *out_;
    splitter_type       splitter_;
    bool                at_first_;

public:
    ostream_join_iterator(ostream_type& out)
    : out_{&out}
    , splitter_{}
    , at_first_{true}
    {
    }

    /**
     *  Construct from an ostream.
     *
     *  @remarks The delimiter is not copied, and thus must not be destroyed
     *          while this iterator is in use.
     *
     *  @param  out         Underlying ostream to write to.
     *  @param  splitter    CharT delimiter string to insert.
     */
    ostream_join_iterator(ostream_type& out, const char_type* splitter)
    : out_{&out}
    , splitter_{splitter}
    , at_first_{true}
    {
    }

    /// Copy constructor.

    ostream_join_iterator(const ostream_join_iterator& other)
    : out_{other.out_}
    , splitter_{other.splitter_}
    , at_first_{other.at_first_}
    {
    }

    ostream_join_iterator& operator=(const T& value)
    {   // add separator before the value, if not at first element
        if((not splitter_.empty()) and (not at_first_))
            *out_ << splitter_;
        *out_ << value;
        at_first_ = false;
        return *this;
    }

    ostream_join_iterator& operator=(ostream_join_iterator temp)
    {
        using std::swap;
        swap(out_, temp.out_);
        swap(splitter_, temp.splitter_);
        swap(at_first_, temp.at_first_);
        return *this;
    }

    ostream_join_iterator&
    operator*() {
        return *this;
    }

    ostream_join_iterator&
    operator++() {
        return *this;
    }

    ostream_join_iterator&
    operator++(int) {
        return *this;
    }
};

template <typename T> using join = ostream_join_iterator<T>;
template <typename T> using wjoin = ostream_join_iterator<T, wchar_t>;

#endif  /* OSTREAM_JOIN_ITERATOR_HPP */

用法:

using namespace std;
ostringstream   sql;
sql << "SELECT ";
transform(fields_.begin(), fields_.end(), join<string>{sql, ", "},
    [](const field& f) { return f.escaped_name(); });
sql << " FROM " << name_;

转换调用可以很容易地调整为一个便利的函数。

【讨论】:

    猜你喜欢
    • 2014-12-16
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 2019-10-16
    • 2015-01-14
    • 2014-10-15
    • 1970-01-01
    • 2011-07-11
    相关资源
    最近更新 更多