c++ 函数指针 & lambda & namespace & thread & 计时 & unit test & visual unit test

2023-03-17

函数指针

#include <iostream>

void Hello_world() {
	std::cout << "hello world!" << std::endl;
}

int main() {
	typedef void(*HelloWorld)();
	auto func = Hello_world;
	void (*p)() = &Hello_world;
	func();
	p();
	HelloWorld c = Hello_world;
	c();
}

函数指针还可以这样用

#include <iostream>
#include <vector>

void Hello_world(int value) {
	std::cout << "hello world! value:" <<value<< std::endl;
}

void PrintValue(std::vector<int>&values,void(*func)(int)){
	for (int value : values) {
		func(value);
	}
}

int main() {
	std::vector<int> values = { 1,2,3,4,5 };
	PrintValue(values, Hello_world);
}

lambda函数

lambda函数其实是一种匿名函数,我们写一个简短的函数,又不会在其他地方用,就适合用它。
上面的内容可以改成如下形式

#include <iostream>
#include <vector>
#include <algorithm>

void Hello_world(int value) {
	std::cout << "hello world! value:" <<value<< std::endl;
}

void PrintValue(std::vector<int>&values,void(*func)(int)){
	for (int value : values) {
		func(value);
	}
}

int main() {
	std::vector<int> values = { 1,2,3,4,5 };
	PrintValue(values, [](int value) {std::cout << value << std::endl; });
}

lambda的更详细信息可以去https://en.cppreference.com/w/
[]用于捕获其他变量,常用的有=(值传入所有变量) &(引用传入所有变量)
()用于传参
{}就是一个普通函数体

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

void Hello_world(int value) {
	std::cout << "hello world! value:" <<value<< std::endl;
}

void PrintValue(std::vector<int>&values,std::function<void(int)>func){
	for (int value : values) {
		func(value);
	}
}

int main() {
	int a = 10;
	std::vector<int> values = { 1,2,3,4,5 };
	PrintValue(values, [=](int value) {std::cout << value << std::endl; std::cout << a << std::endl; });
}

但是注意的是,通过值传递是无法修改变量的,在函数体内也不能做类似于a=5的操作
添加mutable关键字后就可以了。

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

void Hello_world(int value) {
	std::cout << "hello world! value:" <<value<< std::endl;
}

void PrintValue(std::vector<int>&values,std::function<void(int)>func){
	for (int value : values) {
		func(value);
	}
}

int main() {
	int a = 10;
	std::vector<int> values = { 1,2,3,4,5 };
	PrintValue(values, [=](int value) mutable {a = 5; std::cout << value << std::endl; std::cout << a << std::endl; });
	std::cout << a << std::endl; //当然不改变值传递的本质,仍然为10
}

通过引用传递的是会修改的

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

void Hello_world(int value) {
	std::cout << "hello world! value:" <<value<< std::endl;
}

void PrintValue(std::vector<int>&values,std::function<void(int)>func){
	for (int value : values) {
		func(value);
	}
}

int main() {
	int a = 10;
	std::vector<int> values = { 1,2,3,4,5 };
	PrintValue(values, [&](int value) {a = 5; std::cout << value << std::endl; std::cout << a << std::endl; });
	std::cout << a << std::endl; //是5
}

lambda作为迭代器的判断条件

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
int main() {
	int a = 10;
	std::vector<int> values = { 1,2,3,4,5 };
	auto it = std::find_if(values.begin(), values.end(), [](int value) {return value > 3; });
	std::cout << it[1] << std::endl;
}

namespace

namespace其实就起一个标识函数的作用

#include<iostream>
#include <string>
#include <algorithm>

namespace apple {
	void print(const std::string& text) {
		std::cout << text << std::endl;
	}
}

namespace orange {
	void print(const char* text) {
		std::string temp = text;
		std::reverse(temp.begin(), temp.end());
		std::cout << temp << std::endl;
	}
}

int main() {
	apple::print("wsxk");
	orange::print("wsxk");
	using namespace apple;
	namespace a = apple;
	print("wsxk");
	a::print("wsxk");
}

thread

thread其实主要是用来并发的执行某个事情时的工具

#include <iostream>
#include <thread>

bool is_finished = false;

void Dowork() {
	std::cout << "thread id=" << std::this_thread::get_id() << std::endl;// 获得线程id
	while (!is_finished) {
		std::cout << "working..." << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(1));//睡眠1s
	}
}

int main() {
	std::thread worker(Dowork);//创建线程,传入执行函数,创建后就开始工作
	std::cin.get();
	is_finished = true;
	worker.join();// wait until the thread ends

	std::cout << "thread id=" << std::this_thread::get_id() << std::endl;

	return 0;
}

计时

计时是一种非常有效的测试手段,可以帮助我们直观看到算法的耗时,以及测试用时等等
这是一种平台无关的测试方法.

#include <iostream>
#include <chrono>
#include <thread>

int main() {
	auto start = std::chrono::high_resolution_clock::now();
	std::this_thread::sleep_for(std::chrono::seconds(1));
	auto end = std::chrono::high_resolution_clock::now();

	std::chrono::duration<float> duration = end - start;
	std::cout << duration.count() << "s" << std::endl;
}

有个更简单的方法,利用构造/析构函数自动计时

#include <iostream>
#include <chrono>
#include <thread>

class Timer {
	std::chrono::steady_clock::time_point start, end;
	std::chrono::duration<float> duration;
public:
	Timer() {
		start = std::chrono::high_resolution_clock::now();
	}
	~Timer() {
		end = std::chrono::high_resolution_clock::now();
		duration = end - start;
		float ms = duration.count() * 1000.0f;
		std::cout << "Timer took " << ms << "ms" << std::endl;
	}
};

void Function() {
	Timer time;
	for (int i = 0; i < 100; i++) {
		//std::cout << "hello world\n";
		std::cout << "hello world" << std::endl;
	}
}

int main() {
	Function();
}

这里有一个有趣的点,其实std::endl是很费时的
单单改了一个符号,就能让程序执行速度快很多!

unit test

单元测试是一个很重要的部分,通常用来测试这个代码优化的怎么样,运行速度如何,和其他方法的速度比较等等。
原理和计时一样。
值得一提的是,一般都在release模式下进行测试,debug模式添加了很多安全措施

#include <iostream>
#include <chrono>
class Timer {
public:
	Timer() {
		m_start = std::chrono::high_resolution_clock::now();
	}
	~Timer() {
		stop();
	}
	void stop() {
		m_end = std::chrono::high_resolution_clock::now();
		auto start = std::chrono::time_point_cast<std::chrono::microseconds>(m_start).time_since_epoch().count();
		auto end = std::chrono::time_point_cast<std::chrono::microseconds>(m_end).time_since_epoch().count();
		auto duration = end - start;
		double ms = duration * 0.001;
		std::cout << ms << "ms" << std::endl;
	}
private:
	std::chrono::time_point<std::chrono::high_resolution_clock> m_start;
	std::chrono::time_point<std::chrono::high_resolution_clock>	m_end;
};
int main() {
	{
		Timer TIME;
		int value = 0;
		for (int i = 0; i < 1000000; i++) {
			value += 2;
		}
	}
	//std::cout << value << std::endl;
}

测试new和make_shred

#include <iostream>
#include <chrono>
#include <array>
class Timer {
public:
	Timer() {
		m_start = std::chrono::high_resolution_clock::now();
	}
	~Timer() {
		stop();
	}
	void stop() {
		m_end = std::chrono::high_resolution_clock::now();
		auto start = std::chrono::time_point_cast<std::chrono::microseconds>(m_start).time_since_epoch().count();
		auto end = std::chrono::time_point_cast<std::chrono::microseconds>(m_end).time_since_epoch().count();
		auto duration = end - start;
		double ms = duration * 0.001;
		std::cout << ms << "ms" << std::endl;
	}
private:
	std::chrono::time_point<std::chrono::high_resolution_clock> m_start;
	std::chrono::time_point<std::chrono::high_resolution_clock>	m_end;
};
int main() {
	struct Vector2 {
		float x, y;
	};
	{
		std::array<std::shared_ptr<Vector2>, 1000> ShredPtr;
		Timer time;
		for (int i = 0; i < 1000; i++) {
			ShredPtr[i] = std::make_shared<Vector2>();
		}
	}
	{
		std::array<std::shared_ptr<Vector2>, 1000> ShredPtr;
		Timer time;
		for (int i = 0; i < 1000; i++) {
			ShredPtr[i] = std::shared_ptr<Vector2>(new Vector2());
		}
	}
	//std::cout << value << std::endl;
}

事实表明,make_shared速度比new快一些

unique_ptr 和 shared_ptr对比

#include <iostream>
#include <chrono>
#include <array>
class Timer {
public:
	Timer() {
		m_start = std::chrono::high_resolution_clock::now();
	}
	~Timer() {
		stop();
	}
	void stop() {
		m_end = std::chrono::high_resolution_clock::now();
		auto start = std::chrono::time_point_cast<std::chrono::microseconds>(m_start).time_since_epoch().count();
		auto end = std::chrono::time_point_cast<std::chrono::microseconds>(m_end).time_since_epoch().count();
		auto duration = end - start;
		double ms = duration * 0.001;
		std::cout << ms << "ms" << std::endl;
	}
private:
	std::chrono::time_point<std::chrono::high_resolution_clock> m_start;
	std::chrono::time_point<std::chrono::high_resolution_clock>	m_end;
};
int main() {
	struct Vector2 {
		float x, y;
	};
	{
		std::array<std::shared_ptr<Vector2>, 1000> ShredPtr;
		Timer time;
		for (int i = 0; i < 1000; i++) {
			ShredPtr[i] = std::make_shared<Vector2>();
		}
	}
	{
		std::array<std::unique_ptr<Vector2>, 1000> ShredPtr;
		Timer time;
		for (int i = 0; i < 1000; i++) {
			ShredPtr[i] = std::make_unique<Vector2>();
		}
	}
	
}

unique_ptr要快一些

visual unit test

取自:https://gist.github.com/TheCherno/31f135eea6ee729ab5f26a6908eb3a5e
代码如下:

//
// Basic instrumentation profiler by Cherno

// Usage: include this header file somewhere in your code (eg. precompiled header), and then use like:
//
// Instrumentor::Get().BeginSession("Session Name");        // Begin session 
// {
//     InstrumentationTimer timer("Profiled Scope Name");   // Place code like this in scopes you'd like to include in profiling
//     // Code
// }
// Instrumentor::Get().EndSession();                        // End Session
//
// You will probably want to macro-fy this, to switch on/off easily and use things like __FUNCSIG__ for the profile name.
//
#pragma once

#include <string>
#include <chrono>
#include <algorithm>
#include <fstream>

#include <thread>

struct ProfileResult
{
    std::string Name;
    long long Start, End;
    uint32_t ThreadID;
};

struct InstrumentationSession
{
    std::string Name;
};

class Instrumentor
{
private:
    InstrumentationSession* m_CurrentSession;
    std::ofstream m_OutputStream;
    int m_ProfileCount;
public:
    Instrumentor()
        : m_CurrentSession(nullptr), m_ProfileCount(0)
    {
    }

    void BeginSession(const std::string& name, const std::string& filepath = "results.json")
    {
        m_OutputStream.open(filepath);
        WriteHeader();
        m_CurrentSession = new InstrumentationSession{ name };
    }

    void EndSession()
    {
        WriteFooter();
        m_OutputStream.close();
        delete m_CurrentSession;
        m_CurrentSession = nullptr;
        m_ProfileCount = 0;
    }

    void WriteProfile(const ProfileResult& result)
    {
        if (m_ProfileCount++ > 0)
            m_OutputStream << ",";

        std::string name = result.Name;
        std::replace(name.begin(), name.end(), '"', '\'');

        m_OutputStream << "{";
        m_OutputStream << "\"cat\":\"function\",";
        m_OutputStream << "\"dur\":" << (result.End - result.Start) << ',';
        m_OutputStream << "\"name\":\"" << name << "\",";
        m_OutputStream << "\"ph\":\"X\",";
        m_OutputStream << "\"pid\":0,";
        m_OutputStream << "\"tid\":" << result.ThreadID << ",";
        m_OutputStream << "\"ts\":" << result.Start;
        m_OutputStream << "}";

        m_OutputStream.flush();
    }

    void WriteHeader()
    {
        m_OutputStream << "{\"otherData\": {},\"traceEvents\":[";
        m_OutputStream.flush();
    }

    void WriteFooter()
    {
        m_OutputStream << "]}";
        m_OutputStream.flush();
    }

    static Instrumentor& Get()
    {
        static Instrumentor instance;
        return instance;
    }
};

class InstrumentationTimer
{
public:
    InstrumentationTimer(const char* name)
        : m_Name(name), m_Stopped(false)
    {
        m_StartTimepoint = std::chrono::high_resolution_clock::now();
    }

    ~InstrumentationTimer()
    {
        if (!m_Stopped)
            Stop();
    }

    void Stop()
    {
        auto endTimepoint = std::chrono::high_resolution_clock::now();

        long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
        long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch().count();

        uint32_t threadID = std::hash<std::thread::id>{}(std::this_thread::get_id());
        Instrumentor::Get().WriteProfile({ m_Name, start, end, threadID });

        m_Stopped = true;
    }
private:
    const char* m_Name;
    std::chrono::time_point<std::chrono::high_resolution_clock> m_StartTimepoint;
    bool m_Stopped;
};

生成的result.json 拖入 chrome浏览器chrome://tracing即可分析
还有一个衍生版本:
https://github.com/GavinSun0921/InstrumentorTimer