C++ SFINAE 检测是否存在某个名称的成员函数

SFINAE(Substitution failure is not an error) 被用在很多模板的花式操作里,笔者使用的时候多半是为了将运行时的多态替换成编译器的静态多态。 简单版:检测一个类T是否存在某名字的成员函数 举个例子,有如下的类定义: 希望能有一个静态的检测,能够实现 那么可以这样[1]: 基本逻辑是如果被测试的typename T有成员函数,那么调用HasSomeWhatFunction的静态方法test<T>()时,模板替换是按照返回值类型为one(char)来生效的;反之如果不存在SomeWhatFunction的函数,那么返回类型one的模板替换失败,继续查找,匹配到万能重载(catch-all overload) 的static two test(…)里。 这种做法的可读性在模板的骚操作里算是好的,但是它无法区分SomeWhatFunction的函数签名(参数类型、返回值类型)。 普通版:检测一个类T是否存在某签名的成员函数 接上个例子拓展一下: 这个就需要更复杂一些的逻辑了: 可以看到这个实现比之前的多定义了一个内部类Check。在初始化enum value值时会调用test,然后会尝试模板替换,进而想要去特化struct Check。如果失败了(不存在对应签名的函数),那么会回滚到static two test里。 这个实现其实能够覆盖许多场景了,但是还有一个奇怪的需求满足不了:如果要判断一个类的模板成员函数呢? 地狱版:检测一个类T是否存在某签名的模板成员函数 小改一下SomeWhatClass 如果需要检测void SomeTempMemberFunc<int, T&>是否存在,咋办? 搜了一圈,解释的最清楚的是参考[2]里面描述的,利用std::declval的解法。 几个知识点: decltype里利用了含有逗号运算符[3]的表达式; declval可以避开构造函数而使用类成员; 参考 [1] writing and using a C++ template to check for a function’s existence http://www.cplusplus.com/forum/beginner/70134/ [2] SFINAE Hell: detecting…

[C++] 在linux或windows上使用direct io

首先 Direct IO是一种不用内核缓存的IO, 它可以做到直接将用户空间的内存直接写入磁盘或者将磁盘数据直接读到用户空间的缓冲区,这种策略就是不用内核的缓存而使用用户自己设计的缓存. 需要注意的是,使用DirectIO会完全绕过系统的预取(prefetch)以及页缓存机制,如果不是必须,那么我认为还是优先考虑普通的read或者直接mmap吧。 Linux 几个方面注意一下就可以了 在调用[open]1时,把O_DIRECT加上。比如int fd = open("/path/to/file", O_DIRECT, O_RDONLY); 用于文件读写的buffer,必须和磁盘的块大小对齐(保守起见一般可以设为4KB)。有两种方法能拿到地址对齐的内存块: 直接使用posix_memalign; 直接new一段内存,然后根据返回的内存地址,往后找到第一个满足对齐要求的地址就可以。这种方法会浪费前面一段空间,不过其实posix_memalign在系统操作的时候”浪费”了; 借用mmap申请MAP_ANONYMOUS匿名映射,addr参数填NULL的话mmap出来的地址是页对齐的(至少是4K对齐),所以可以直接拿来用; 如果需要lseek之类的操作,注意seek的文件位置偏移量必须是磁盘块大小的整数倍; 2.4内核下, 每次文件读写的长度必须是块大小的整数倍(e.g. N * 4KB). Linux 2.6.0+无此要求; Windows下 参考这篇文章,可以知道Windows下也可以启用类似的机制,对应的打开文件flag是FILE_FLAG_NO_BUFFERING.用法类似,与Linux不同的地方在于: 在CreateFileA(…)调用的dwFlagsAndAttributes参数里把FILE_FLAG_NO_BUFFERING填上; 用于文件读写的buffer也是需要对齐的 也可以使用类似的方法_aligned_malloc申请对齐的内存,但是必须注意要使用_aligned_free释放内存,否则runtime error;

Boost Python的C++对象, Pickle支持及其原理

默认用boost python包裹的C++对象是不支持pickle的,如果要用pickle.dumps(obj)的话那会提示错误 Pickling of "xxx" instances is not enabled. 这边吐槽一下在最新的代码里,给的reference链接其实还是不可用的。真正正确的是https://www.boost.org/doc/libs/1_74_0/libs/python/doc/html/reference/topics/pickle_support.html。 让你的class支持Pickle协议 若要让你的C++ Class支持Pickle协议,比较“正统”的方法是利用boost提供的boost::python::pickle_suite. 拿代码说话: struct world_t { world_t(const string& country) { … } }; struct world_pickle_suite : boost::python::pickle_suite { static boost::python::tuple getinitargs(const world_t& w) { // [可选实现] 返回一个boost::python::tuple元组,其值被用来构造 // 如果一个类的构造函数**不需要参数**的话,可以不用重载这个方法。 return boost::python::make_tuple(w.country()); } static boost::python::tuple getstate(const world_t& w) { // [可选实现] 如果对象的构造函数并不能完全恢复对象的状态, // 那么要用此函数返回其状态值 }…

记录一下找了半天的huge page坑——fork越来越慢的原因

背景 之前发现Jupyter Notebook下面,如果数据占用多的话,开多进程池会特别的慢。一开始以为是Python的锅,但是把multiprocessing.pool改成直接用os.fork()调用以后,问题依旧。照理来说unix下面使用fork开进程,会启用copy-on-write机制,内存增长并不是特别明显,但是实际在htop下面看内存仍然会在fork之后增长,并且和进程数量是线性相关的。 原因 随后想了老半天,想到了可能和页表有关系。查了一下,跑的服务器上huge page确实被禁用了(不知为何…). fork的机制简单地说,是在创建新进程的时候把老的进程控制块(Process Control Block)里内存页表拷贝给了新的PCB——这边具体内存的信息是不拷贝的。由于当时Notebook跑的数据处理任务,里面已经用了不少内存(100GB+),所以拷贝的时候如果用默认的4KB内存页,将会有100 * 1024 * 1024 / 4 = 104,857,600个页表! 按典型一个页表项(Page Table Entry)大小4Bytes计算,一个进程开出来光页表会耗400MB内存.

Support parallel XZ decompression for unix (7zip LZMA SDK based, C/C++)

为unix平台增加XZ多线程解压缩支持(基于7zip LZMA SDK, C/C++) Note This post has nothing to do with the pixz project. I am talking about decompressing the original xz archive using 7-zip’s LZMA SDK under unix environment. Background Originally the 7zip’s LZMA SDK (version 19.00) only covers parallel xz decompression for Windows systems. This post shows the C code that adds…

std::future 与 std::promise 简单使用、简单原理

C++11有了一些关于线程的模型,在此之前C++里可是各自为政的,各种线程库各种神奇用法。其中有两个好玩的东西就是std::promise<T>和std::future<T>,下文书中形容它们像是个“虫洞”。 std::future是虫洞的出口:一个未来对象的“获取器”,在未来的某一刻它能返回一个有用的值,但现在还没有… std::promise是虫洞的入口:我们要保证在未来的某一刻给一个值进去,但不是现在… Basic usage 一般来说这俩是成对使用的,看个代码: #include <cassert> #include <chrono> #include <future> #include <iostream> #include <string> #include <thread> using namespace std::chrono_literals; int main() { std::promise<int> p1, p2; std::future<int> f1 = p1.get_future(); std::future<int> f2 = p2.get_future(); p1.set_value(42); assert(f1.get() == 42); std::thread t([&]() { std::this_thread::sleep_for(100ms); p2.set_value(43); }); auto start_time = std::chrono::system_clock::now(); assert(f2.get() == 43); std::chrono::duration<double, std::milli> elapsed…

C++17 std::variant 简单使用

std::variant应当是用来替代union来使用的,后者从C时代就有了,但是它缺乏类型安全的保障。boost库很早之前就有了实现(其实从C++11开始,感觉boost不知道多少东西被借鉴走了)。std::variant的类长这个样子: template <class… Types> class variant; Usage 使用起来也是比较方便的 std::variant<int, double> v1; v1 = 1; // activate the "int" member assert(v1.index() == 0); assert(std::get<0>(v1) == 1); v1 = 3.14; // activate the "double" member assert(v1.index() == 1); assert(std::get<1>(v1) == 3.14); assert(std::get<double>(v1) == 3.14); assert(std::holds_alternative<int>(v1) == false); assert(std::holds_alternative<double>(v1) == true); assert(std::get_if<int>(&v1) == nullptr); assert(*std::get_if<double>(&v1) == 3.14); 上面代码里的几个好用的constexpr函数:…

C++17 std::generate的使用

std::generate是“遍历——执行”工具函数的一种 #include <algorithm> #include <iostream> #include <string> #include <vector> int main() { std::vector<std::string> v(4); std::generate(v.begin(), v.end(), [i=0]() mutable { return ++i % 2 ? "hello" : "world"; }); for (auto&& s : v) { std::cout << s << " "; } return 0; } 上面的函数输出是hello world hello world . std::generate(beginIt, endIt, generator)函数接受起止的迭代器以及一个生成方法。

C++17 新特性—— if constexpr

看代码说话 template <typename Iterator> auto distance(Iterator begin, Iterator end) { using Traits = std::iterator_traits<Iterator>; if constexpr (std::is_base_of_v<std::random_access_iterator_tag, typename Traits::iterator_category>) { return end – begin; } else { auto result = typename Traits::difference_type(); for (auto it = begin; it != end; ++it) { ++result; } return result; } } 上面的代码用来判断两个iterator之间的距离。主要逻辑是判断iterator是否是可随机访问的iterator然后作分支处理,可以随机访问的迭代器直接把iterator相减。在constexpr if语句中,条件的值必须是可按语境转换到 bool 类型的经转换常量表达式。若其值为 true,则舍弃 false分支语句(若存在),否则舍弃 true分支语句。有意思的一点是,被舍弃语句中的…