layout | title |
---|---|
post |
第四期 |
从reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态。
每周更新
欢迎投稿,推荐或自荐文章/软件/资源等,请提交 issue
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更快。有时间可以琢磨一下