在我的项目中,我有一些功能,如
std::tuple<VAO, Mesh, ShaderProgram> LoadWavefront(std::string filename);我可以这样用:
VAO teapotVAO;
Mesh teapotMesh;
ShaderProgram teapotShader;
std::tie(teapotVAO, teapotMesh, teapotShader)
= LoadWavefront("assets/teapot.obj");问题是,这要求这些类中的每个类都有一个默认构造函数,该构造函数在无效状态下创建它们,这很容易出错。我怎样才能不需要对每一项进行std::get<>,就能避开这些问题呢?有什么优雅的方法吗?
发布于 2015-03-12 13:53:10
我怎样才能不需要对每一项进行std::get<>,就能避开这些问题呢?有什么优雅的方法吗?
按值返回,而不是按“值”返回(这个std::tuple允许您这样做)。
API更改:
class Wavefront
{
public:
Wavefront(VAO v, Mesh m, ShaderProgram sp); // use whatever construction
// suits you here; you will
// only use it internally
// in the load function, anyway
const VAO& vao() const;
const Mesh& mesh() const;
const ShaderProgram& shader() const;
};
Wavefront LoadWavefront(std::string filename);发布于 2014-11-21 19:31:52
有一个倒置控制流的风格,可能是有用的。
LoadWavefront("assets/teapot.obj", [&]( VAO teapotVAO, Mesh teapotMesh, ShaderProgram teapotShader ){
// code
});使用VAO&引用样式而不是可选的。在这种情况下,可以将lambda的返回值用作LoadWavefront的返回值,默认的lambda将所有3个参数都转发出去,如果您愿意,允许“旧样式”访问。如果你只想要一个,或者想在加载之后做一些事情,你也可以这样做。
现在,LoadWavefront可能应该返回一个future,因为它是一个IO函数。在这种情况下,future of tuple。我们可以使上面的模式更加通用:
template<class... Ts, class F>
auto unpack( std::tuple<Ts...>&& tup, F&& f ); // magic并这样做
unpack( LoadWavefront("assets/teapot.obj"), [&]( VAO teapotVAO, Mesh teapotMesh, ShaderProgram teapotShader ){
// code
});还可以教授unpack关于std::future的知识,并自动创建结果的未来。
这可能导致一些令人讨厌的括号级别。如果我们想疯掉的话,我们可以从函数式编程中窃取一页:
LoadWavefront("assets/teapot.obj")
*sync_next* [&]( VAO teapotVAO, Mesh teapotMesh, ShaderProgram teapotShader ){
// code
};其中LoadWavefront返回一个std::future<std::tuple>。命名操作符*sync_next*在左侧获取一个std::future,在右边获取一个lambda,协商一个调用约定(首先试图将tuples压平),并将future作为延迟调用继续进行。(请注意,在windows上,async返回的async在销毁时失败了,这违反了标准)。
然而,这是一种疯狂的做法。在建议的await中,可能会有更多类似于这种类型的代码,但是它将提供更清晰的语法来处理它。
无论如何,这里是一个名为infix *then*的操作符的完整实现,仅仅是因为实例化
#include <utility>
#include <tuple>
#include <iostream>
#include <future>
// a better std::result_of:
template<class Sig,class=void>
struct invoke_result {};
template<class F, class... Args>
struct invoke_result<F(Args...), decltype(void(std::declval<F>()(std::declval<Args>()...)))>
{
using type = decltype(std::declval<F>()(std::declval<Args>()...));
};
template<class Sig>
using invoke_t = typename invoke_result<Sig>::type;
// complete named operator library in about a dozen lines of code:
namespace named_operator {
template<class D>struct make_operator{};
template<class T, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, Op>&& lhs, Rhs&& rhs )
-> decltype( invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
// create a named operator then:
static struct then_t:named_operator::make_operator<then_t> {} then;
namespace details {
template<size_t...Is, class Tup, class F>
auto invoke_helper( std::index_sequence<Is...>, Tup&& tup, F&& f )
-> invoke_t<F(typename std::tuple_element<Is,Tup>::type...)>
{
return std::forward<F>(f)( std::get<Is>(std::forward<Tup>(tup))... );
}
}
// first overload of A *then* B handles tuple and tuple-like return values:
template<class Tup, class F>
auto invoke( Tup&& tup, then_t, F&& f )
-> decltype( details::invoke_helper( std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f) ) )
{
return details::invoke_helper( std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f) );
}
// second overload of A *then* B
// only applies if above does not:
template<class T, class F>
auto invoke( T&& t, then_t, F&& f, ... )
-> invoke_t< F(T) >
{
return std::forward<F>(f)(std::forward<T>(t));
}
// support for std::future *then* lambda, optional really.
// note it is defined recursively, so a std::future< std::tuple >
// will auto-unpack into a multi-argument lambda:
template<class X, class F>
auto invoke( std::future<X> x, then_t, F&& f )
-> std::future< decltype( std::move(x).get() *then* std::declval<F>() ) >
{
return std::async( std::launch::async,
[x = std::move(x), f = std::forward<F>(f)]() mutable {
return std::move(x).get() *then* std::move(f);
}
);
}
int main()
{
7
*then* [](int x){ std::cout << x << "\n"; };
std::make_tuple( 3, 2 )
*then* [](int x, int y){ std::cout << x << "," << y << "\n"; };
std::future<void> later =
std::async( std::launch::async, []{ return 42; } )
*then* [](int x){ return x/2; }
*then* [](int x){ std::cout << x << "\n"; };
later.wait();
}这将使您可以执行以下操作:
LoadWaveFront("assets/teapot.obj")
*then* [&]( VAO teapotVAO, Mesh teapotMesh, ShaderProgram teapotShader ){
// code
}我觉得很可爱。
发布于 2014-11-21 19:07:27
你可以用boost::optional
boost::optional<VAO> teapotVAO;
boost::optional<Mesh> teapotMesh;
boost::optional<ShaderProgram> teapotShader;
std::tie(teapotVAO, teapotMesh, teapotShader)
= LoadWavefront("assets/teapot.obj");当然,您必须更改访问这些值的方式,以始终执行*teapotVAO,但如果您搞砸了任何访问,编译器至少会让您知道。
https://stackoverflow.com/questions/27068396
复制相似问题