-
Notifications
You must be signed in to change notification settings - Fork 15.5k
Open
Labels
Description
Motivation
Many C++ codebases still use C-style function pointers (return_type (*)(parameters)) for callbacks and strategy patterns:
// BAD - Legacy C-style function pointer
void register_callback(void (*callback)(int, void*), void* user_data);
// BAD - Function taking raw function pointer
void process_data(int* array, size_t size, int (*transform)(int));
This approach is inflexible and cannot work with:
- Lambda expressions with captures;
- Function objects (functors);
std::bindexpressions;std::mem_fnexpressions.
register_callback([&](int value, void*) { /* lambda with capture */ }, d); // won't be compiled
process_data(arr, n, [](int x) { return x * 2; }); // won't be compiled
Proposed Solution
Add a clang-tidy check that identifies raw function pointer parameters and suggests converting them to more flexible alternatives:
// GOD - converted to std::function
void register_callback(const std::function<void(int)>& callback);
// GOD - converted to template version
template<typename F>
void process_data(int* array, size_t size, F&& transform);
Since the first option can negatively impact performance, it's better to use the second one whenever possible—in static functions and functions declared in A-files. However, the second option can lead to binary bloat, so it shouldn't be used for very large functions.
Special Cases
- C API Compatibility
// Should NOT warn when extern "C" or __cdecl specified
extern "C" {
void take_c_api_callback(void (*callback)(int)); // No warning
}