赞
踩
最近发现一个视频列表,名为“C++ Weekly With Jason Turner”。其中每个视频都不长,是一个大佬每周做一次在线教程,围绕C++的一些细节特性进行现场编程,空闲时间看看解解闷挺好的,还能学到不少技巧。我试着复现一些教程中用到的实例,打算看到哪做到哪吧。下文中和以后的文章中所有代码都将出现在我的这个repo。
每个教程都会有一个文件夹,内有源文件和一个Build.sh
脚本,该脚本用于简单地编译源文件。
Jason在这次教程中使用的是随机数生成作为一个task对待,我嫌麻烦,就换成了两个嵌套的循环做一些无意义的运算,总之时间消耗能看出来就行。
使用std::async
时,Jason推荐明确写出std::launch::async
已达到明确的异步执行的目的。
#include <iostream> #include <future> static long int add_sequence(int start, int end) { long int res = 0; for ( int j = 0; j < 2; j++ ) { for ( int i = start; i < end; i++ ) { res += static_cast<int>(i * 1.0); } } return res; } static void serial() { auto res0 = add_sequence(0, 1000000000); auto res1 = add_sequence(1000000000, 2000000000 ); std::cout << "res0 = " << res0 << '\n'; std::cout << "res1 = " << res1 << '\n'; } static void async() { auto f0 = std::async( std::launch::async, add_sequence, 0, 1000000000 ); auto f1 = std::async( std::launch::async, add_sequence, 1000000000, 2000000000 ); std::cout << "f0.get() = " << f0.get() << '\n'; std::cout << "f1.get() = " << f1.get() << '\n'; } static void throws() { std::runtime_error err("This is an exception."); throw err; } static void async_exception() { auto f = std::async( std::launch::async, throws ); try { f.get(); } catch ( const std::exception& ex ) { std::cout << ex.what() << '\n'; } } int main( int argc, char** argv ) { std::cout << "Hello, Future! \n"; // serial(); async(); // async_exception(); return 0; }
在我目前的电脑上,serial()
版本和async()
版本的可执行文件,通过/usr/bin/time
输出的结果如下
Hello, Future!
res0 = 999999999000000000
res1 = 2999999999000000000
6.53user 0.00system 0:06.54elapsed 99%CPU (0avgtext+0avgdata 3468maxresident)k
0inputs+0outputs (0major+141minor)pagefaults 0swaps
Hello, Future!
f0.get() = 999999999000000000
f1.get() = 2999999999000000000
6.58user 0.00system 0:03.31elapsed 198%CPU (0avgtext+0avgdata 3784maxresident)k
0inputs+0outputs (0major+149minor)pagefaults 0swaps
这std::async
确实挺方便。Jason还提到了异常的处理,也挺方便的,我上面也做了一个小测试。在第11期中,Jason提醒我们,future.get()将会block,直到它代表的运算完成。有时候在调用std::async()
时,可以使用std::launch::deferred
,此时运算是lazy的,意味着运算只有当get()
函数被调用时才会发生。调用std::async()
时也可以不指定如何执行(async或者deferred),系统会“自动”选择执行方式。
Jason 在这期和第6期中对variadic template的用法进行了说明,他推荐一个使用initializer list的方法替代通常的递归的方式处理variadic template, 挺有趣的。
在测试时,Jason特殊强调不通话编译器在对待函数的参数的evaluation次序上的行为是不同的,但是逗号表达试和initializer list是会确保evaluation次序。
上述采用initializer list的方法适用于C++17之前,在C++17时,可以直接利用fold expression对variadic template parameter进行展开,这在第20期里做了讲解。
#include <iostream> #include <sstream> #include <vector> template< typename ... T > std::vector<std::string> convert_2_str( const T& ... args ) { std::stringstream ss; std::vector<std::string> vs; (void)std::initializer_list<int>{ ( ss.str(""), ss << args, vs.push_back(ss.str()), 0 )... }; return vs; } template < typename... T > std::vector<std::string> convert_2_str_fold( const T&... args ) { std::stringstream ss; return { ( ss.str(""), ss << args, ss.str() )... }; } int main( int argc, char** argv ) { std::cout << "Hello, VariadicExpansion! \n"; auto vs = convert_2_str( "Hello", "world", 1, 2.2f, 3.3 ); for ( const auto& s : vs ) { std::cout << s << '\n'; } auto vsf = convert_2_str_fold( "Hello", "world", 1, 2.2f, 3.3 ); for ( const auto& s: vs ) { std::cout << s << '\n'; } std::cout << "Done! \n"; return 0; }
执行结果如下
Hello, VariadicExpansion!
Hello
world
1
2.2
3.3
Hello
world
1
2.2
3.3
Done!
看起来,无论如何,需要在代码的某个地方,罗列出某个操作支持的所有type才行。如下代码参考了Jason的第12期视频,另外我自己参考了std::any
的官方文档,设计了下面的示例。在示例中,我们将std::string
,int
,float
和double
类型的变量存入同一个std::vector
中,这就有点奇幻了。然后试图对非std::string
类型的数据做加1运算,并希望该运算改变vector中保存的数值。Jason提醒过,通过使用std::any_cast<T>(std::any*)
能够获得指向目标数据的指针,然而似乎没有获取指向目标数据的引用的方法。
要想针对特定的type做特定的运算,类别是不能做到完全透明的。或许利用type traits可以实现,但是我目前还不太理解。
#include <any> #include <functional> #include <iomanip> #include <iostream> #include <unordered_map> #include <string> #include <typeindex> #include <typeinfo> #include <type_traits> #include <vector> using namespace std::string_literals; // Build a map for handling simple numerics. template< typename T, typename F > inline std::pair< const std::type_index, std::function<void(std::any& a)> > to_any_visitor(F const& f) { return { std::type_index( typeid(T) ), [ g = f ] ( std::any& a ) { if constexpr ( std::is_void_v<T> ) { g(); } else { g( std::any_cast<T>(&a) ); } } }; } static std::unordered_map< std::type_index, std::function<void(std::any&)> > any_visitor { to_any_visitor<void>([]{ std::cout << "{}"; }), to_any_visitor<int>([](int* a){ *a += 1; std::cout << "int visitor\n"; }), to_any_visitor<float>([](float* a){ *a += 1.f; std::cout << "float visitor\n"; }), to_any_visitor<double>([](double* a){ *a += 1.0; std::cout << "double visitor\n"; }) }; inline void process( std::any& a ) { if ( const auto it = any_visitor.find( std::type_index(a.type()) ); it != any_visitor.end() ) { it->second(a); } else { std::cout << "Unregistered type: " << std::quoted( a.type().name() ) << '\n'; } } int main( int argc, char** argv ) { std::cout << "Hello, Any! \n"; std::vector<std::any> v { "Hello"s, 1, 2.2f, 3.3 }; for ( const auto& a : v ) { std::cout << "a.type().name() = " << a.type().name() << '\n'; } for ( auto& a : v ) { process(a); } std::cout << "std::any_cast<double>(v[3]) = " << std::any_cast<double>(v[3]) << '\n'; return 0; }
上述代码执行结果如下(管道链接到 c++filt -t)
Hello, Any!
a.type().name() = std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
a.type().name() = int
a.type().name() = float
a.type().name() = double
Unregistered type: "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >"
int visitor
float visitor
double visitor
std::any_cast<double>(void[3]) = 4.3
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。