tldr;如何实现用std::index_sequence
运行定时函数的for循环?
好吧,我承认这个标题有点神秘,但我一直在考虑这个问题:在编译时有可能在运行时甚至编译时有for循环吗?
我可能对std::index_sequence
可能做的事情太兴奋了。我将解释我的目标是什么。我想要类似以下代码的东西:
for(int i = 1; i < 100000; ++i)
{
auto start = time();
runOpenCL<i>();
std::cout << time() - start << std::endl;
}
编译到这个(每个定时器):
runOpenCL<1>();
runOpenCL<2>();
runOpenCL<3>();
...
runOpenCL<100000>();
现在我觉得这应该行得通吗?因为for循环通常在编译时以这种方式解释(如果这是正确的短语)。然而,我知道模板对这种可能的不可靠代码有一定的保护措施,所以我看到std::index_sequence可以绕过这一点,但我对模板代码的了解还不够,无法弄清楚发生了什么。现在,在有人说我可以把它作为一个正常的函数参数之前,是的,如果你看看函数本身,我可以做到:
template<int threadcount>
INLINE void runOpenCL()
{
constexpr int itemsPerThread = (MATRIX_HEIGHT + threadcount - 1) / threadcount;
// executing the kernel
clObjs.physicsKernel.setArg(2, threadcount);
clObjs.physicsKernel.setArg(3, itemsPerThread);
clObjs.queue.enqueueNDRangeKernel(clObjs.physicsKernel, cl::NullRange, cl::NDRange(threadcount), cl::NullRange);
clObjs.queue.finish();
// making sure OpenGL is finished with its vertex buffer
glFinish();
// acquiring the OpenGL object (vertex buffer) for OpenCL use
const std::vector<cl::Memory> glObjs = { clObjs.glBuffer };
clObjs.queue.enqueueAcquireGLObjects(&glObjs);
// copying the OpenCL buffer to the BufferGL
clObjs.queue.enqueueCopyBuffer(clObjs.outBuffer, clObjs.glBuffer, 0, 0, planets_size_points);
// releasing the OpenGL object
clObjs.queue.enqueueReleaseGLObjects(&glObjs);
}
但我不想。我需要更好的理由吗?我认为实现这一点会非常酷。只要它最终仍然可读。
下面是一个可能的版本,它将使用C++17倍表达式展开循环:
#include <type_traits>
#include <utility>
template <std::size_t I>
void runOpenCL();
template <std::size_t... Is>
void runAllImpl(std::index_sequence<Is... >) {
// thanks @Franck for the better fold expression
(runOpenCL<Is>(), ...);
}
void runAll() {
runAllImpl(std::make_index_sequence<10000>{});
}
没有C++17,你可以做这样的事情,但在未优化的构建中,你会得到一个巨大的堆栈爆炸:
#include <type_traits>
#include <utility>
template <std::size_t I>
void runOpenCL();
template <std::size_t... Is>
void runAllImpl(std::index_sequence<Is... >) {
int arr[]{ (runOpenCL<Is>(), 0)... };
(void)arr;
}
void runAll() {
runAllImpl(std::make_index_sequence<10000>{});
}
这似乎比@康桓瑋'但是(至少)GCC不能编译1000000(10000是"可以的")。
您可以在编译时生成一个固定大小的函数表,并通过运行时索引调用表中相应的函数。例如:
#include <array>
template<std::size_t N>
constexpr auto gen_func_table = []<std::size_t... Is>
(std::index_sequence<Is...>) {
return std::array{+[] { runOpenCL<Is>(); }...};
}(std::make_index_sequence<N>{});
int main() {
constexpr std::size_t max_count = 100;
constexpr auto& func_table = gen_func_table<max_count>;
for(int i = 1; i < max_count; ++i)
func_table[i]();
}
演示。