0

c++ template declaration & definition in different files.

// interface.hpp
template <typename F, typename... Args>
constexpr bool is_static_task_v =
    is_invocable_r_v<void, F, Args...> && !is_invocable_r_v<int, F, Args...>;

template <class F, class... Args,
            std::enable_if_t<is_static_task_v<F, Args...>, void>* = nullptr>
static void enqueue(uint32_t service_id, F&& f, Args&&... args);

// interface.cpp
template <class F, class... Args,
          std::enable_if_t<is_static_task_v<F, Args...>, void>*>
void enqueue(uint32_t service_id, F&& f, Args&&... args) {
...
}
/***** explicit instantiate template ***/
template 
void enqueue<>(unsigned int, void (*&&)());

when i explicit instantiate template, it's not usefull for lambada function? how to resolve?

for example

// main.cpp
void foo() {std::cout << "foo" << std::endl;}
enqueue(1,&foo)   // it works
enqueue(1,[](){std::cout << "foo" << std::endl;} // it link error

I know it need to explicit instatiate template, but i don't know how to instatiate this template.

the clang error: enqueue(uint32_t, F&&, Args&& ...) [with F = main(int, const char**)::<lambda()>; Args = {}; std::enable_if_t<is_static_task_v<F, Args ...>, void>* <anonymous> = 0; uint32_t = unsigned int]

And I instatiate template for lambda like this, it still no work:

template void enqueue<void (*)(), , (void*)0>(unsigned int,void (*&&)());

1 Answers1

0

The type of [](){std::cout << "foo" << std::endl;} is of some anonymous closure type. Let's call it Lambda. So, enqueue(1,[](){std::cout << "foo" << std::endl;}) calls enqueue<Lambda>(Lambda&&), not enqueue<void(*)()>(void(*&&)()) like you've explicitly instantiated.

You need to turn it into a void(*)() function pointer, you can do it one of these ways (See: Obtaining function pointer to lambda?):

void(*fp)() = [](){std::cout << "foo" << std::endl;};
enqueue(1, std::move(fp));

// or
enqueue(1, static_cast<void(*)()>([](){std::cout << "foo" << std::endl;}));

// or
enqueue(1, +[](){std::cout << "foo" << std::endl;});

But you probably should not have template definitions in source files. You could instead do this with overloads:

// interface.hpp
void enqueue(uint32_t service_id, void f());
//other functions...

// interface.cpp
namespace {
template <class F, class... Args,
          std::enable_if_t<is_static_task_v<F, Args...>, void>*>
void enqueue_impl(uint32_t service_id, F&& f, Args&&... args) {
  //...
}
}

// All your explicit instantiations replaced with overload that
// just calls the above template
void enqueue(uint32_t service_id, void f()) {
  enqueue_impl(service_id, f);
}

Or just move the contents of interface.cpp to a header file (some people do this with a .tpp/.ipp "template/implementation" file, or a .inc file. A plain .hpp file works fine too)

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • thanks, it works. But there's still one small problem, the interface.cpp can't move to be a header file(.tpp/.ipp/.inc), when use google Bazel to compile this file. – hustyangsan Jul 01 '22 at 06:43
  • @hustyangsan have a read of [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Richard Critten Jul 01 '22 at 08:38