Programming Suggestion
Do not use Macro to pre-process source file
In legacy c99
one common skill is to use #if
to conditionally remove code piece, and this is done in the compiler pre-process stage. In other words, the same effect as removing that part of code directly from the file.
The down side of this technique is that modern IDE lost awareness of that part of code and some operations like rename an variable, refactor function definition totally missed those code pieces. After sometime, another person can not easily turn off those #if
easily because of big code change, lost context, etc.
But how to achieve similar effect? I personally do it like following example:
// use static const (or constexpr) to serve MACRO purpose
static const bool ENABLE_LOG = 0==1;
int has_three_args(int argc){
return 3==argc;
}
int main(int argc, char *argv[]){
// use `if` statement with compile time deterministic condition to serve `#if` purpose
if (1==1 && ENABLE_LOG){
return has_three_args(argc);
}
return 0;
}
Try compile it with minimal as O1 level optimization, c++ -c -O1 main.cpp && objdump -dr main.o
. You can see the if
section is completely removed from main
function. The same effect as #if
achieved.
Disassembly of section .text:
0000000000000000 <_Z14has_three_argsi>:
0: 83 ff 03 cmp $0x3,%edi
3: 0f 94 c0 sete %al
6: 0f b6 c0 movzbl %al,%eax
9: c3 retq
000000000000000a <main>:
a: b8 00 00 00 00 mov $0x0,%eax
f: c3 retq
CoreDump debugging
To simulate a segment fault crash,
- Run
ulimit -c unlimited
to enable core dump saving. - Start the executable like
catchsegv sleep 1m
(catchsegv
help print the stack trace info when crash). pgrep -af sleep
to find out thePID
of the executable.kill -11 $PID
to send aSIGSEGV(11)
signal to the process to trigger crash.- Find core dump file under
/var/lib/systemd/coredump/
. The path or saving pattern can be configured with likeecho '|/root/bin/pipe-script.py %P' > /proc/sys/kernel/core_pattern
. - Analysis the core dump with
gdb --tui -q /usr/bin/sleep ./core.sleep
.
Easy to make mistake
-
需要在
std
命名空间内重载STL容器(如std::vector
)的比较操作符,否则不会被诸如std::set
等容器自动应用。 -
类数组delete 基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用 delete 和 delete[] 没有区别。但是对于类对象数组,只能用 delete[]。
-
switch语句中case跳过变量初始化的问题
case语句中不允许直接定义变量,因为当此case条件不被执行时变量将无法初始化。解决的办法是case后面的结构用{}括起来以限制变量的作用域。如
switch (a) { case 0: POINTS p = *(POINTS *)a; int i = 0; break; case 1: break; }
编译报错:
error C2360: initialization of 'i' is skipped by 'case' label
用这样写才对:
switch (a) { case 0: { POINTS p = *(POINTS *)a; int i = 0; break; } case 1: break; }
-
Do not predfine
istream_iterator
as the The first object is read when the iterator is constructed:std::istream_iterator<float> inputIter(inputFile);
Code snippets
-
Solve static variable in class template can not be partial specification.
template<typename T> T _default_value() { return T(); } template<> float _default_value<float>() { return -1; } //typedef int T; template<typename T> class CL { public: typedef decltype(fabs(T() - T())) TD; public: CL() {} ~CL() {} TD get(int i, int j) { return default; } private: static const TD default; }; template<typename T> const typename CL<T>::TD CL<T>::default = _default_value<CL<T>::TD>();
-
Get value type of pointer
#include <iostream> template<typename T> struct DECAY { typedef decltype(**(T*)NULL) type; }; int main(int argc, char *argv[]) { const char *cc=NULL; typename DECAY<decltype(cc)>::type x = *cc; //reference, nothing happend when x is accessed, so no runtime error here std::cout << typeid(x).name() << std::endl; typename std::decay<decltype(*cc)>::type y=*cc; //assign, runtime error when cc==NULL std::cout << typeid(y).name() << std::endl; }
-
Singleton
#include <iostream> #include <memory> #include <map> class Singleton { public: static Singleton& GetInstance(size_t id = 0) { static std::map<size_t, std::unique_ptr<Singleton>> mpInstance; if (mpInstance.find(id) == mpInstance.end()) { printf("New singleton request: %d \n", id); mpInstance[id] = std::unique_ptr<Singleton>(new Singleton(id)); //unique_ptr will make sure new got delete and not pointer transfered } else { printf("Old singleton request: %d \n", id); } return *mpInstance[id]; } ~Singleton() { printf("I am died: %d -> %x\n", id, this); } private: size_t id; Singleton(size_t _id):id(_id){ printf("I am borned: %d -> %x\n", id, this); } Singleton(const Singleton&) {} Singleton(Singleton&&) {} }; int main(int argc, char *argv[]) { ErrorCode e, result = SUCCESS; printf("Main started\n"); Singleton& x = Singleton::GetInstance(0); printf("Got it: %d -> %x\n", 0, &x); Singleton& y = Singleton::GetInstance(1); printf("Got it: %d -> %x\n", 1, &y); Singleton& z = Singleton::GetInstance(0); printf("Got it again: %d -> %x\n", 0, &z); printf("Main exit\n"); return 0; }
boost
example#include <iostream> #include <fstream> #include <iomanip> #include <boost/filesystem.hpp> int main(int argc, char *argv[]) { const bool debug_print = true; if (debug_print) { debug_fp = new std::ofstream("1.txt", std::ios::app); } if (debug_print) { *debug_fp << std::setprecision(10) << 1.0 << "\n"; } if (debug_print) { debug_fp->close(); delete debug_fp; boost::filesystem::copy_file(".\\1.txt", ".\\2.txt"); } return 0; }
std::multimap
for count unique values#include <iostream> #include <map> int main(int argc, char *argv[]) { std::multimap<int, size_t> keyvalue; // initialize for (size_t j = 0; j < 19; j++) { keyvalue.insert(std::make_pair(j%5, j)); } // count unique keys auto key = keyvalue.begin()->first; const size_t nkey = 1 + std::count_if(keyvalue.begin(), keyvalue.end(), [&key](const auto& s) { if (s.first != key) { key = s.first; return true; } else { return false; } }); // iterate every key group auto kv = keyvalue.begin(); for (size_t g = 0; kv != keyvalue.end(); kv = keyvalue.upper_bound(kv->first), ++g) {//for every key group auto krange = keyvalue.equal_range(kv->first); std::cout<<"Got "<<std::distance(krange.first, krange.second)<<" values with key" << kv->first << std::endl; for (auto kiter = krange.first; kiter != krange.second; kiter++) {// for every value std::cout << " " << kiter->first << " - " << kiter->second << std::endl; } } return 0; }
- Mordern C++: Reference
/********************************************************************* std::vector<class>::push_back and std::make_pair **********************************************************************/ class PairC { public: PairC() { std::cout << "Created " << count << " times."; count++; } ~PairC() { std::cout << "Deleted " << count << " times."; count--; } private: static int count; }; int PairC::count = 0; std::vector<PairC> ReturnPair() { std::vector<PairC> x; x.resize(2); return std::move(x); } int main(int argc, char *argv[]) { std::vector<std::pair<int, std::vector<PairC>>> x; x.push_back(make_pair(1, ReturnPair())); return 0; } /********************************************************************* Move constructor **********************************************************************/ #include <iostream> #include <vector> class ITEM { public: ITEM() { id = _id++; std::cout << "item " << id << " created\n"; } ~ITEM() { std::cout << "item " << id << " removed\n"; } int id; static int _id; private: }; int ITEM::_id = 0; std::vector<ITEM> right_reference(int nth) { std::vector<ITEM> x(2); return std::move(x); } class ST { public: ST(int nth):vec(right_reference(nth)) { } ~ST() { std::cout << " -- " << vec.size() << " items left -- \n"; } ST(const ST& v):vec(v.vec) { } ST(ST&& v):vec(std::move(v.vec)) { } std::vector<ITEM> vec; private: }; ST right_reference_VEC(int nth) { ST x(nth); return std::move(x); } int main(int argc, char *argv[]) { ST x = right_reference_VEC(2); std::cout << " -- " << x.vec.size() << " items here -- \n"; std::cout << "item " << x.vec[0].id << " read\n"; std::cout << "item " << x.vec[1].id << " read\n"; return 0; } /********************************************************************* Reference in STL container **********************************************************************/ #include <iostream> #include <vector> #include <algorithm> #include <iterator> class ObjTracker { public: ObjTracker() { id = n++; p = this; std::cout<<"raw construct "<<id<<" : "; say(); } ~ObjTracker(){ std::cout<<"raw destroy "<<id<<" : "; say(); p = nullptr; } ObjTracker(const ObjTracker&) = delete; ObjTracker(ObjTracker&&) = delete; void say() const { std::cout<<"I am "<<id<<" with $"<<p<<"$"<<std::endl; } static void report() { std::cout<<"==== Total "<<n<<" objects created ===="; } private: static int n; int id; void* p; }; int ObjTracker::n=0; class ObjTrackerWrapper { public: ObjTrackerWrapper() = delete; // ObjTrackerWrapper(const ObjTrackerWrapper&) = delete; // ObjTrackerWrapper(ObjTrackerWrapper&&) = delete; ObjTrackerWrapper(const ObjTrackerWrapper& ohs):content(ohs.content){ p = this; std::cout<<"wrapper rh construct : "; say(); }; ObjTrackerWrapper(ObjTrackerWrapper&& ohs):content(ohs.content){ p = this; std::cout<<"wrapper move construct : "; say(); }; ObjTrackerWrapper(ObjTracker& obj):content(obj){ p = this; std::cout<<"wrapper normal construct : "; say(); } ~ObjTrackerWrapper(){ std::cout<<"wrapper destroy : "; say(); } void say() const { std::cout<<"I am wrapper "<<p<<" for $"<<&content<<"$ , it say : "; content.say(); } private: ObjTracker& content; void* p; }; class WatchDog { public: WatchDog() {} ~WatchDog(){ ObjTracker::report(); } } _; int main() { std::cout<<"==== main started ===="<<std::endl; std::vector<ObjTracker> a(2); std::cout<<"==== raw created ===="<<std::endl; std::vector<ObjTrackerWrapper> b; std::cout<<"==== wrapper created ===="<<std::endl; std::copy( a.begin(), a.end(), std::back_insert_iterator<std::vector<ObjTrackerWrapper>>(b) ); std::cout<<"==== copy completed ===="<<std::endl; for(auto& i: b){ i.say(); } std::cout<<"==== main exit here ===="<<std::endl; return 0; }
-
Designated initializer C99 not supported in C++11
struct MyStruct { int a; int b; }; MyStruct x = {.a=1, .b=2} ;
-
在字符串中删除某个字符,STL的妙用
#include <iostream> #include <string> #include <algorithm> #include <functional> using namespace std; int main(int argc, const char *argv[]) { string str(" d df df "); string::iterator new_end = remove_if(str.begin(), str.end(), bind2nd(equal_to<char>(), ' ')); str.erase(new_end, str.end()); cout << str << endl; return 0; }
Tips:
-
STL internal: deconstructor can be called explicity. See
xmemory0
for howstd::vector
destruct an object when.pop_back
with the help ofstd::allocator
template<class _Uty> void destroy(_Uty *_Ptr) { // destroy object at _Ptr _Ptr->~_Uty(); // **this line of code will be skiped for build-in types** }
-
Use command
cpp -dM -E -xc++ /dev/null
andcc -dM -E -xc /dev/null
to check build-in macros. Or check macros definiation place in the source code while processinggcc -E $CompileOptions $SourceFile | \ egrep "^# " | grep -v '<'| cut -f 2 -d '"' | \ sort | uniq | while read line do grep -l $MacroName $line done
- Dealing with macro in C++
// use g++ -E main.cpp -o main.i to pre-process the file #include <iostream> void __print_macro_value__() { #define __PRINT__MACRO__(x) std::cout<<"value of "<<(#x)<<" is "<<(x)<<std::endl __PRINT__MACRO__(__cplusplus); #undef __PRINT__MACRO__ }
-
In gdb, use
set print elements {200|unlimited}
andprint *{pArray}@{len}
to print all the contents ofpArray[]
. Usep (char*)m_path
to print a string. -
Ubuntu安装Sqlite3
安装数据库系统Sqlite3:
apt-get install sqlite sqlite3 #注:在程序中使用Sqlite3
编译还需要安装Sqlite3编译需要的工具包:
apt-get install libsqlite3-dev
包含头文件
#include <sqlite3.h>
,编译时在连接器选项中加入选项-lsqlite3