Including .cpp files
thrift 的required、optional探究
gdb 调试利器
【Eigen】从入门到放弃(一):Eigen是个什么鬼?
命令行参数:
#include <gflags/gflags.h>
DEFINE_string(conf_file, "conf/testing.conf", "config file");
gflags::ParseCommandLineFlags(&argc, (char***)&argv, true);
conf::init(FLAGS_conf_file);
捕获异常:
try {
throw std::runtime_error("input doc error");
} catch (std::exception& ex) {
LOGF_ERROR("Exception thrown! reason: %s", ex.what());
return -1;
} catch (...) {
LOGF_ERROR("Exception thrown! reason unknown");
return -1;
}
move用法:
C++11右值引用和std::move语句实例解析
std::move()实际应用分析
请勿使用return std::move(obj)返回右值引用
std::make_move_iterator 把*iterator的返回值转换成右值
std::vector<std::string> v1(s.begin(), s.end()); // copy
std::vector<std::string> v2(std::make_move_iterator(s.begin()),
std::make_move_iterator(s.end())); // move
#include <vector>
struct A {
int a;
int x;
};
int main() {
using namespace std;
A a1;
A a2;
vector<A> va;
va.push_back(a1); //单独使用push back,会完全将a1 copy一份
va.push_back(move(a2)); //push back+move,会新建变量,但是变量内部的数据来自于a2内部的,使用move后a2就不可用了
}
make share用法:
shared_ptr是一种智能指针(smart pointer),作用有如同指针,但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数(reference counting),比如我们把只能指针赋值给另外一个对象,那么对象多了一个智能指针指向它,所以这个时候引用计数会增加一个,我们可以用shared_ptr.use_count()函数查看这个智能指针的引用计数,一旦最后一个这样的指针被销毁.
Difference in make_shared and normal shared_ptr in C++
智能指针之shared_ptr基本概述
智能指针shared_ptr的reset用法
#include <cpputil/program/conf.h>
extern std::shared_ptr<cpputil::program::Conf> conf;
conf = std::make_shared<cpputil::program::Conf>(filename);
auto servable = std::shared_ptr<Servable>(new EmbeddingServable(FLAGS_model));
int a = new int(100);
std::shared_ptr ptr(a);
std::shared_ptr<int> ptr1 = std::make_shared<int>(15);
std::shared_ptr<int> ptr2(ptr1);
//std::shared_ptr<int> ptr2 = ptr1;这样赋值是错误的,只要是智能指针,这样直接用=赋值是有问题的必须std::shared_ptr<int> ptr2(ptr1);
Difference in make_shared and normal shared_ptr in C++
std::shared_ptr<int> ptr1 = std::make_shared<int>(15);
shared_ptr<string> ss(new string("AAA"));
shared_ptr<string> ss = shared_ptr<string>(new string("AAA"));
shared_ptr<string> ss;
ss.reset(new string("AAA"));
数据格式转换:
#include <boost/lexical_cast.hpp>
boost::lexical_cast<int>(value);
try {
return boost::lexical_cast<double>(vs);
} catch (boost::bad_lexical_cast) {
return default_val;
}
# 字符串拆分
#include <boost/algorithm/string.hpp>
boost::algorithm::split(str_gpus, FLAGS_gpus, boost::algorithm::is_any_of(","));
路径:
::boost::filesystem::path model_root(conf::get_str("model_root"));
::boost::filesystem::path vocab2id_path8(model_root);
vocab2id_path8.append("s1.3.txt", boost::filesystem::path::codecvt());
std::vector<std::pair<std::string, std::string>> pb_path_vec{
std::make_pair("s1.3", vocab2id_path8.string()),
};
无锁队列:
#include <concurrentqueue/blockingconcurrentqueue.h>
query_queue_ = std::make_shared<moodycamel::BlockingConcurrentQueue<Item>>(10240);
vector 用法:
std::vector<std::thread> threads_seq2seq;
for (int i=0;i<num_threads_seq2seq;++i) {
threads_seq2seq.emplace_back(std::thread([]{Singleton<Seq2seqServing>::instance().run();}));
}
(1): vector<int> ilist1;
默认初始化,vector为空, size为0,表明容器中没有元素,而且 capacity 也返回 0,意味着还没有分配内存空间。这种初始化方式适用于元素个数未知,需要在程序中动态添加的情况。
(2): vector<int> ilist2(ilist);
vector<int> ilist2 = ilist;
两种方式等价 ,ilist2 初始化为ilist 的拷贝,ilist必须与ilist2 类型相同,也就是同为int的vector类型,ilist2将具有和ilist相同的容量和元素
(3): vector<int> ilist = {1,2,3.0,4,5,6,7};
vector<int> ilist {1,2,3.0,4,5,6,7};
ilist 初始化为列表中元素的拷贝,列表中元素必须与ilist的元素类型相容,本例中必须是与整数类型相容的类型,整形会直接拷贝,其他类型会进行类型转换。
(4): vector<int> ilist3(ilist.begin()+2,ilist.end()-1);
ilist3初始化为两个迭代器指定范围中元素的拷贝,范围中的元素类型必须与ilist3 的元素类型相容,在本例中ilist3被初始化为{3,4,5,6}。注意:由于只要求范围中的元素类型与待初始化的容器的元素类型相容,因此迭代器来自不同的容器是可能的,例如,用一个double的list的范围来初始化ilist3是可行的。另外由于构造函数只是读取范围中的元素进行拷贝,因此使用普通迭代器还是const迭代器来指出范围并没有区别。这种初始化方法特别适合于获取一个序列的子序列。
(5): vector<int> ilist4(7);
默认值初始化,ilist4中将包含7个元素,每个元素进行缺省的值初始化,对于int,也就是被赋值为0,因此ilist4被初始化为包含7个0。当程序运行初期元素大致数量可预知,而元素的值需要动态获取的时候,可采用这种初始化方式。
(6):vector<int> ilist5(7,3);
指定值初始化,ilist5被初始化为包含7个值为3的int
std::vector<GroupBuilder> groups_
groups_.emplace_back(engine, group, vocab);
匿名函数
auto fill_one_field = [](FieldInfo& field_info) { do something }
Lambda 表达式本质上与函数声明非常类似。Lambda 表达式具体形式如下:
[capture](parameters)->return-type{body}
例如:
[](int x, int y){ return x < y ; }
如果没有返回值可以表示为:
[capture](parameters){body}
例如:
[]{ ++global_x; }
在一个更为复杂的例子中,返回类型可以被明确的指定如下:
[](int x, int y) -> int { int z = x + y; return z + x; }
本例中,一个临时的参数 z 被创建用来存储中间结果。如同一般的函数,z 的值不会保留到下一次该不具名函数再次被调用时。
如果 lambda 函数没有传回值(例如 void),其返回类型可被完全忽略。
在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:
[] // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
另外有一点需要注意。对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入:
[this]() { this->someFunc(); }();
int x = 4;
auto y = [&r = x, x = x + 1]()->int
{
r += 2;
return x * x;
}(); // updates ::x to 6 and initializes y to 25.
指针的指针
int main(int argc, char* const argv[]) {
gflags::ParseCommandLineFlags(&argc, (char***)&argv, true);
return run_thrift_server(argc, (char**)argv);
}
class A
{
public:
A() = default;//正确
A(int _a) = default;//错误,构造函数需要没有默认参数
int getA() = default;//错误,仅适用于类的特殊成员函数
}
class A
{
public:
A();
A(const A&) = delete; // 声明拷贝构造函数为 deleted 函数
A& operator = (const A &) = delete; // 声明拷贝赋值操作符为 deleted 函数
};
线程池:
#include <thread>
tp_ = std::make_shared<ThreadPool>();
tp_->start(static_cast<size_t>(conf::get_int("thread_pool_size", 16)));
omp编程:
OpenMP多线程linux下的使用,简单化
并行计算之OpenMP中的任务调度
#inlcude <omp.h>
#pragma omp parallel for
#pragma omp for reduction(+: 变量)
#pragma omp critical//锁
{
}
#pragma omp parallel for private(x,y)//每个线程都独立拷贝x, y变量,互不干扰,如果不设默认是共享变量
#pragma omp parallel for schedule(static/dynamic/guided, k)//总工作量划分成n/k块,再多线程调度
#pragma omp parallel sections
{
#pragma omp section//要保证几个section下的函数之间没有变量依赖
.........
#pragma omp section
.........
}
#pragma omp parallel
{
.......();
#pragma omp master/single //保证只有主线程/某个线程能访问下面的函数,区别是使用master没有barrier珊障,single的话先完成的线程等待没完成的线程
{
}
.......
}
#pragma omp barrier/nowait //强制设置珊障/无需等待,如果后续函数对前面的多线程没有依赖,即可使用nowait
#pragma omp parallel for firstprivate(变量)/lastprivate(变量) //为每个多线程赋初值/出多线程回到主线程时赋值供主线程使用
std::function&std::bind&std::future:
聊聊C++异步编程-2
std::function为函数模板类,可以认为是函数的类型声明。
C++ std::function
#include <functional>
std::function<bool(int, int)> fun; //fun变量的类型是输入为int int,输出为bool的函数
std::bind用于包装普通函数,返回std::function类型,bind默认参数为值传递
std::ref和std::cref使用
std::ref 用于包装按引用传递的值。
std::cref 用于包装按const引用传递的值。
#include <functional>
void add_func(int &x, const int &y, int& z) {
x++;
y++;
z++;
}
auto func = std::bind(add_func, std::ref(x), std::cref(y), z);
std::future对象在内部存储一个将来会被赋值的值,并提供了一个访问该值的机制,通过get()成员函数实现。如果有人试图在get()函数可用之前通过它来访问相关的值,那么get()函数将会阻塞,直到该值可用。
c++11多线程编程(八):std::future , std::promise和线程的返回值
std::vector<std::future<int>> rets;
rets = server_->batch_submit();
int ret = rets[i].get();
完整使用:
std::vector<std::function<int()>> task_list;
for (size_t i = 0; i < speeds; i++) {
task_list.push_back(std::bind(&Index::search, this, //绑定多个函数
std::cref(ctx), std::ref(aaa[i])));
}
std::vector<folly::Future<int>> futures;
for (size_t i = 1; i < tasks.size(); ++i) {
auto f = folly::via(&executor_, tasks[i]); //为线程池提交任务
futures.push_back(std::move(f));
}
if (tasks[0]() != 0) {
err_cnt += 1;
}
for (auto& f : futures) {
int ret = std::move(f).get(); //判断并行任务是否完成
}
namespace:
header中使用
Using c++ typedef/using type alias
别再让C++头文件中出现“using namespace xxx;”
using Server = AAA::MultiThreadServer;
namespace whatevs
{
enum Enum
{
FOO = 0,
BAR,
BLARGH,
MEH,
SIZE
};
} // namespace whatevs
namespace on = whatevs;
#include "whatevsenum.hpp"
void do_something_with(on::Enum value)
{
// do stuff
}
do_something_with(on::FOO);
或者 using namespace::whatevs::Enum;
Enum::FOO;
可变(变长)模板:
模板类型可以不定长度
泛化之美–C++11可变模版参数的妙用
#include <iostream>
#include <typeinfo>
#include <typeindex>
#include <vector>
using namespace std;
template <class... T>
void expand(T... args)
{
vector<type_index> types = {std::type_index(typeid(T))...}; //展开类型
int arr[] = {(cout<<args<<" ", 0)...}; //展开值
}
template <class... T>
void expandB()
{
vector<type_index> types = {std::type_index(typeid(T))...}; //展开类型
}
int main() {
expand<int, int, int, int>(1,2,3,4); //展开值 cout 1 2 3 4 arr 0 0 0 0
// 可以不用类型
expand(5,6,7); //展开值 cout 5 6 7 arr 0 0 0
// 可以不用参数
expandB<int, int, int>(); //展开值 cout 5 6 7
return 0;
}
boost::any:
存储任意类型
boost::any 用法
inputs[i] = outputs[idx]; //存入任意类型
std::type_index(typeid(field)) != std::type_index(inputs[idx].type())) //校验类型
boost::any_cast<decltype(field)>(message_->inputs[idx]); //转换类型
inserter:
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_set>
int main()
{
std::vector<int> input({ 1, 2, 2, 1, 3, 1, 4 });
std::unordered_set<int> set;
std::copy(input.begin(),
input.end(),
std::inserter(set, set.end()));
for (const int &i: set) {
std::cout << i << " ";
}
return 0;
}
大量和少量去重对比:
What’s the most efficient way to erase duplicates and sort a vector?
f1: Just using vector, sort + unique
sort( vec.begin(), vec.end() );
vec.erase( unique( vec.begin(), vec.end() ), vec.end() );
f2: Convert to set (using a constructor)
set<int> s( vec.begin(), vec.end() );
vec.assign( s.begin(), s.end() );
f3: Convert to set (manually)
set<int> s;
for (int i : vec)
s.insert(i);
vec.assign( s.begin(), s.end() );
f4: Convert to unordered_set (using a constructor)
unordered_set<int> s( vec.begin(), vec.end() );
vec.assign( s.begin(), s.end() );
sort( vec.begin(), vec.end() );
f5: Convert to unordered_set (manually)
unordered_set<int> s;
for (int i : vec)
s.insert(i);
vec.assign( s.begin(), s.end() );
sort( vec.begin(), vec.end() );
前置声明:
随机数:
C++11随机数的正确打开方式
How to shuffle a std::vector?
#include <iostream>
#include <algorithm>
#include <random>
using namespace std;
int main()
{
std::vector<int> a = {0,1,2,3,4,5,6,7,8,9};
// 使用rd产生的随机种子,因为种子不确定,rng每次输出不同数值序列,每次shuffle后vector序不一样
std::random_device rd;
std::default_random_engine rng(rd());
// 使用1作为种子,因为种子确定,rng每次输出相同数值序列,每次shuffle后vector序一样
// std::default_random_engine rng(1);
std::shuffle(std::begin(a), std::end(a), rng);
for(int i=0; i<10; i++)
cout << a[i] << endl;
return 0;
}
函数后面的const &:
Using ‘const’ in class’s functions
const:说明this指针是一个pointer to const object。如果一个对象为const,它只有权利调用const函数,因为成员变量不能改变。
C++成员函数后加&的作用是什么?
& &&:和const一样,修饰的是*this。其实就是函数重载。
public:
void foo(int)&;
void foo(int)&&;
void foo(int)const;
};
void foo(Test&, int);
void foo(Test&&, int);
void foo(const Test&, int);