Cassandra C++驱动回调函数正确实现方式咨询
Hi there! Let's walk through the proper way to implement callbacks with the Cassandra C++ driver, using your code snippet as a starting point.
Key Callback Requirements & Best Practices
1. Match the Required Callback Signature
The Cassandra driver expects callbacks to conform to the CassFutureCallback type definition—this is a non-negotiable rule to avoid undefined behavior. The signature looks like this:
typedef void (*CassFutureCallback)(CassFuture* future, void* data);
Your rtInsertCallback must strictly follow this structure. A valid skeleton for your callback would be:
void rtInsertCallback(CassFuture* future, void* user_data) { // Callback logic goes here }
2. Properly Handle the Future in the Callback
Once the callback triggers, you have three critical tasks to complete:
- Check operation status: Use
cass_future_error_code(future)to verify if the query succeeded or failed. - Retrieve results/errors: If successful, fetch results with
cass_future_get_result(future); if failed, pull error details withcass_future_error_message(future). - Free the future: The driver does not automatically clean up the future after the callback runs. You must call
cass_future_free(future)at the end of your callback to avoid memory leaks.
Here's how that looks in practice:
void rtInsertCallback(CassFuture* future, void* user_data) { CassError error_code = cass_future_error_code(future); if (error_code == CASS_OK) { const CassResult* result = cass_future_get_result(future); // Process the result (e.g., get row counts, extract data) cass_result_free(result); std::cout << "Insert operation succeeded!" << std::endl; } else { const char* error_msg; size_t error_msg_len; cass_future_error_message(future, &error_msg, &error_msg_len); std::cerr << "Insert failed: " << std::string(error_msg, error_msg_len) << std::endl; } // Always free the future to prevent memory leaks cass_future_free(future); }
3. Manage User Data Correctly
In your current code, you're passing NULL as the user data parameter. If you need to pass context (like a class instance, operation metadata, or a completion handler), you can pass a pointer here—but you must ensure the data's lifetime extends until the callback executes.
For example, if you're working with a class, you could pass this (and cast it back in the callback):
// Inside your class method CassFuture* l_query_future = cass_session_execute(RtConnectionObj::ms_session, l_stmt); CassError l_returnCode = cass_future_set_callback(l_query_future, rtInsertCallback, this); // Updated callback void rtInsertCallback(CassFuture* future, void* user_data) { RtConnectionObj* instance = static_cast<RtConnectionObj*>(user_data); // Use the instance to access class state or methods // ... rest of the callback logic ... cass_future_free(future); }
Just make sure the RtConnectionObj instance isn't destroyed before the callback runs—if using dynamic allocation, consider smart pointers or reference counting to manage lifetime.
4. Handle Callback Setup Failures
Your code checks the return value of cass_future_set_callback, which is great! If this call returns an error (e.g., CASS_ERROR_LIB_INVALID_STATE), you need to manually free the future, since the driver won't take ownership of it in this case:
CassFuture* l_query_future = cass_session_execute(RtConnectionObj::ms_session, l_stmt); CassError l_returnCode = cass_future_set_callback(l_query_future, rtInsertCallback, NULL); if (l_returnCode != CASS_OK) { std::cerr << "Failed to set callback: " << cass_error_desc(l_returnCode) << std::endl; // Free the future since the driver didn't take ownership cass_future_free(l_query_future); }
Full Working Example
Putting it all together, here's a complete snippet that follows best practices:
#include <cassandra.h> #include <iostream> #include <string> class RtConnectionObj { public: static CassSession* ms_session; void executeInsert() { // Assume l_prepared_stmt is a pre-prepared statement CassStatement* l_stmt = cass_prepared_bind(l_prepared_stmt); // Bind parameters if needed (example) cass_statement_bind_string(l_stmt, 0, "sample_data"); CassFuture* l_query_future = cass_session_execute(ms_session, l_stmt); // Free the statement immediately—execute takes ownership cass_statement_free(l_stmt); CassError l_returnCode = cass_future_set_callback(l_query_future, rtInsertCallback, this); if (l_returnCode != CASS_OK) { std::cerr << "Callback setup failed: " << cass_error_desc(l_returnCode) << std::endl; cass_future_free(l_query_future); } } static void rtInsertCallback(CassFuture* future, void* user_data) { RtConnectionObj* instance = static_cast<RtConnectionObj*>(user_data); CassError error_code = cass_future_error_code(future); if (error_code == CASS_OK) { std::cout << "Insert completed successfully!" << std::endl; // Optional: Notify the instance of completion // instance->onInsertSuccess(); } else { const char* error_msg; size_t msg_len; cass_future_error_message(future, &error_msg, &msg_len); std::cerr << "Insert failed: " << std::string(error_msg, msg_len) << std::endl; } cass_future_free(future); } private: static CassPrepared* l_prepared_stmt; }; // Initialize static members CassSession* RtConnectionObj::ms_session = nullptr; CassPrepared* RtConnectionObj::l_prepared_stmt = nullptr;
内容的提问来源于stack exchange,提问作者Vishal Sharma

