当前位置:   article > 正文

CppWeekly 01, future, vairadic expansion, std::any_cpp weekly

cpp weekly

最近发现一个视频列表,名为“C++ Weekly With Jason Turner”。其中每个视频都不长,是一个大佬每周做一次在线教程,围绕C++的一些细节特性进行现场编程,空闲时间看看解解闷挺好的,还能学到不少技巧。我试着复现一些教程中用到的实例,打算看到哪做到哪吧。下文中和以后的文章中所有代码都将出现在我的这个repo

每个教程都会有一个文件夹,内有源文件和一个Build.sh脚本,该脚本用于简单地编译源文件。

009 Future

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

在我目前的电脑上,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
  • 1
  • 2
  • 3
  • 4
  • 5
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
  • 1
  • 2
  • 3
  • 4
  • 5

std::async确实挺方便。Jason还提到了异常的处理,也挺方便的,我上面也做了一个小测试。在第11期中,Jason提醒我们,future.get()将会block,直到它代表的运算完成。有时候在调用std::async()时,可以使用std::launch::deferred,此时运算是lazy的,意味着运算只有当get()函数被调用时才会发生。调用std::async()时也可以不指定如何执行(async或者deferred),系统会“自动”选择执行方式。

010 Variadic Expansion

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

执行结果如下

Hello, VariadicExpansion! 
Hello
world
1
2.2
3.3
Hello
world
1
2.2
3.3
Done! 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

012 std::any

看起来,无论如何,需要在代码的某个地方,罗列出某个操作支持的所有type才行。如下代码参考了Jason的第12期视频,另外我自己参考了std::any官方文档,设计了下面的示例。在示例中,我们将std::stringintfloatdouble类型的变量存入同一个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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

上述代码执行结果如下(管道链接到 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/383328
推荐阅读
相关标签
  

闽ICP备14008679号