C++ 中文周刊 第4期

reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态。

每周更新

周刊项目地址 github在线地址知乎专栏

欢迎投稿,推荐或自荐文章/软件/资源等,请提交 issue


资讯

编译器信息最新动态推荐关注hellogcc公众号

本周周报github直达

文章

c++20不允许这样的代码

template <typename ...Args>
void log(Args&& ...args, source_location& loc = source_location::current()) { }

log("hello world", 42);

本文讲了几个解决办法

1 手写变参

template <typename T>
void log(T&& arg, source_location& loc = current());
template <typename T, typename U>
void log(T&& t, U&& u, source_location& loc = current());
template <typename T, typename U, typename V>
void log(T&& t, U&& u, V&& v, source_location& loc = current());

非常98

2 显式

#include <iostream>
#include <source_location>
#include <string>

template <typename... Ts>
void log(Ts&&... ts, const std::source_location& loc = std::source_location::current()) {
    std::cout << loc.function_name() << " line " << loc.line() << ": ";
        ((std::cout << std::forward<Ts>(ts) << " "), ...);
        std::cout << '\n';
}

int main() {
    log<int, int, std::string>(42, 100, "hello world");
    log<double, std::string>(10.75, "an important parameter");
}

3 通过构造函数打印,写辅助推导

#include <iostream>
#include <source_location>
#include <string>

template <typename... Ts>
struct log
{    
    log(Ts&&... ts, const std::source_location& loc = std::source_location::current()) {
        std::cout << loc.function_name() << " line " << loc.line() << ": ";
        ((std::cout << std::forward<Ts>(ts) << " "), ...);
        std::cout << '\n';
    }
};

template <typename... Ts>
log(Ts&&...) -> log<Ts...>;

int main() {
    log(42, 100, "hello world");
    log(10.75, "an important parameter");
}

通过构造函数打印,也不是不行,就是很别扭

4 更进一步

#include <iostream>
#include <string_view>
#include <source_location>
#include <fmt/core.h>

struct Logger {
    Logger(std::source_location l = std::source_location::current()) : loc(std::move(l)) { }
    
    template <typename ...Args>
    void debug(std::string_view format, Args&& ...args) {
	    std::cout << fmt::format("{}({}) ", loc.file_name(), loc.line())
                  << fmt::format(format, std::forward<Args>(args)...) << '\n';
    }
    
private:
    std::source_location loc;    
};
 
int main() {
    std::cout << sizeof(std::source_location) << '\n';
    Logger().debug("{}, {}", "hello", "world");
    Logger().debug("{}, {}", 10, 42);
}

5 用tuple

#include <iostream>
#include <source_location>
#include <string>
#include <tuple>

template <typename... Ts>
void log(std::tuple<Ts...> tup, const std::source_location& loc = std::source_location::current()) {
    std::cout << loc.function_name() << " line " << loc.line() << ": ";
    std::apply([](auto&&... args) {
        ((std::cout << args << ' '), ...);
    }, tup);
    std::cout << '\n';
}

int main() {
    log(std::make_tuple(42, 100, "hello world"));
    log(std::make_tuple(10.75, "an important parameter"));
}

6 用stream

7 这里还有点点子 https://cor3ntin.github.io/posts/variadic/

还是讨论future和coroutines

看代码

class api {
 public:
  virtual ~api() = default;
  virtual auto call() const -> int { return {}; }
};

struct fake_api final : api {
  auto call() const -> int override { return 42; }
};

struct stub_api final : api {
  int call_value{};
  auto call() const -> int override { ++call_calls; return call_value; }
  mutable int call_calls{};
};

int main() {
  {
    fake_api api{};
    assert(42 == api.call());
  }

  {
    stub_api api{};
    assert(0 == api.call_calls);
    api.call_value = 43;
    assert(43 == api.call());
    assert(1 == api.call_calls);
  }

  {
    fakeit::Mock<api> api{};
    fakeit::When(Method(api, call)).Return(43);
    auto &mock_api = api.get();
    assert(43 == mock_api.call());
  }
}

讲的非常细节,把编译器处理的整个流程顺了一遍

简单说就是这么一段代码,throw v v本身是不是move的

std::unique_ptr<int> a(std::unique_ptr<int> p)
{
    auto v = std::make_unique<int>(1);
    return v;  // OK: implicit move (since C++11ish)
    return p;  // OK: implicit move (since C++11ish)
}

void b(std::unique_ptr<int> p)
{
    auto v = std::make_unique<int>(1);
    throw v;  // OK: implicit move (since C++14ish)
    throw p;  // OK: implicit move (since C++20)
}

那么这段代码匹配那个函数呢

template<class T>
auto f(T p, int) -> decltype(throw p)
{
    puts("one");  // #1
    throw p;
}

template<class T>
auto f(T p, long) -> void
{
    puts("two");  // #2
    throw p;
}

int main() {
    f(std::make_unique<int>(42), 42);
}

可以Godbolt自己调一下看看

读着读着发现就是magic_get/boost.pfr的方法,利用结构化绑定探测字段

还是讲折叠表达式,几个经典例子

平均数的几种写法

//1 经典
template<typename... Values>
auto average(Values const&... values)
{
    constexpr auto numberOfValues = double{sizeof...(values)};
    static_assert(numberOfValues > 0);
    return (... + values) / numberOfValues;
}
//2 换一种算法
template<typename... Values>
auto average(Values const&... values)
{
    constexpr auto numberOfValues = double{sizeof...(values)};
    static_assert(numberOfValues > 0);
    return (... + (values / numberOfValues));
}

//3 重写2
template<typename Value, typename... Values>
auto average(Value const& value, Values const&... values)
{
    return (value + ... + values) / (1. + sizeof...(values));
}
// 像2那样重写3
template<typename Value, typename... Values>
auto average(Value const& value, Values const&... values)
{
    return ((value / (1. + sizeof...(values))) + ... + (values / (1. + sizeof...(values))));
}

重复动作

比如反复push_back

template<typename T, typename... Ts>
void push_back(std::vector<T>& v, Ts&&... values)
{
    (v.push_back(std::forward<Ts>(values)), ...);
}
push_back(v, 4, 5, 6, 7, 8, 9, 10);

//反向push
template<typename T, typename... Ts>
void push_back(std::vector<T>& v, Ts&&... values)
{
    (..., v.push_back(std::forward<Ts>(values)));
}

for_each

template<typename Function, typename... Values>
auto for_each_arg(Function function, Values&&... values)
{
    return (function(std::forward<Values>(values)),...);
}

for_each_arg([&v](auto&& value){ v.push_back(value); }, 4, 5, 6, 7, 8, 9, 10);

经典的overloaded trick,用于匹配不同的lambda

template<typename... Lambdas>
struct overloaded : public Lambdas...
{
    explicit overloaded(Lambdas... lambdas) : Lambdas(lambdas)... {}

    using Lambdas::operator()...;
};

// c++17可以用推导指引
template<typename... Lambdas>
struct overloaded : public Lambdas...
{
    using Lambdas::operator()...;
};

template<typename... Lambdas> overloaded(Lambdas...) -> overloaded<Lambdas...>;

视频

没啥新鲜的,讲继承的

没意思,讲Command和Strategy设计模式啥区别。没啥区别

讲的concept和特化这些东西。对比差异

介绍他们的sobjectizer框架。一个actor框架。推了很久了,

讨论模板相关的议题,测试调试以及压测所需的解决方案

这里有ppt https://ninkibah.github.io/coding-with-alpacas/Shenanigans/Shenanigans.html

建议直接看ppt

结论

调试需要编译期打印函数名字,有个 deprecated trick 标记一下,涉及到用到的都会有告警,以及constexpr函数gdb能捕捉到,另外就是用code insight或者 metashall之类的工具

测试呢就多用static_assert,也有个always_false trick, 未来可以用concept约束

benchmark可以手写tracer,compile-time benchmark/build-bench.com 以及metabench库

这个是和redpanda的作者的交流。redpanda是kafka的c++实现。而且比kafka更快。有时间可以琢磨一下


本文永久链接

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账