### Basic CMake Project Setup Source: https://github.com/tarscloud/tarscpp/blob/master/servant/script/cmake_http_demo/CMakeLists.txt Sets the minimum CMake version and project name. Essential for any CMake project. ```cmake cmake_minimum_required(VERSION 3.16) project(Demo-DemoServer) ``` -------------------------------- ### Compile and Install TarsCpp Source: https://github.com/tarscloud/tarscpp/blob/master/README.md Standard sequence of commands to clone the repository, configure with CMake, and build the framework. ```bash git clone https://github.com/TarsCloud/TarsCpp.git --recursive cd TarsCpp cmake . make make install ``` -------------------------------- ### Define TarsMock Library and Installation Rules Source: https://github.com/tarscloud/tarscpp/blob/master/mock/CMakeLists.txt Configures the static library build, sets dependencies, and defines installation paths for headers and library artifacts. ```cmake include_directories(../) file(GLOB_RECURSE SRC_FILES *.cpp) add_library(tarsmock STATIC ${SRC_FILES}) add_dependencies(tarsmock tarsservant) install(DIRECTORY . DESTINATION include/mock FILES_MATCHING PATTERN "*.h") install(TARGETS tarsmock RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) ``` -------------------------------- ### TC_ThreadPool Usage Example Source: https://context7.com/tarscloud/tarscpp/llms.txt Demonstrates initializing, configuring, and using TC_ThreadPool to execute various types of tasks and retrieve their results. Includes setting lifecycle callbacks and managing task completion. ```cpp #include "util/tc_thread_pool.h" using namespace tars; // Task function int calculateSum(int a, int b) { return a + b; } class Calculator { public: int multiply(int a, int b) { return a * b; } }; void threadPoolExample() { TC_ThreadPool pool; // Initialize with 5 worker threads pool.init(5); // Optional: Set thread lifecycle callbacks pool.setThreadInitCallback([]() { cout << "Thread " << this_thread::get_id() << " started" << endl; // Initialize thread-local resources }); pool.setThreadDestroyCallback([]() { cout << "Thread " << this_thread::get_id() << " exiting" << endl; // Cleanup thread-local resources }); // Start thread pool pool.start(); // Submit tasks and get futures auto f1 = pool.exec(calculateSum, 10, 20); auto f2 = pool.exec(calculateSum, 30, 40); // Member function binding Calculator calc; auto f3 = pool.exec(bind(&Calculator::multiply, &calc, placeholders::_1, placeholders::_2), 5, 6); // Lambda tasks auto f4 = pool.exec([](const string& msg) { return "Processed: " + msg; }, "hello"); // Get results (blocking) cout << "Sum 1: " << f1.get() << endl; // 30 cout << "Sum 2: " << f2.get() << endl; // 70 cout << "Product: " << f3.get() << endl; // 30 cout << "Lambda: " << f4.get() << endl; // "Processed: hello" // Submit many tasks vector> results; for (int i = 0; i < 100; i++) { results.push_back(pool.exec([i]() { this_thread::sleep_for(chrono::milliseconds(10)); return i * i; })); } // Wait for all tasks pool.waitForAllDone(10000); // Wait up to 10s // Or manually collect results for (auto& f : results) { cout << f.get() << " "; } // Stop pool pool.stop(); } ``` -------------------------------- ### Build Executable with Dependencies Source: https://github.com/tarscloud/tarscpp/blob/master/tools/pb2tarscpp/CMakeLists.txt Configures the build process for the 'pb2tarscpp' executable. It sets the output path, handles Windows-specific definitions, finds source files, links necessary libraries (protobuf and protoc), and defines installation rules. ```cmake set(MODULE "pb2tarscpp") set(EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}/bin") if(WIN32) add_definitions(-DPROTOBUF_USE_DLLS) endif() aux_source_directory(. DIR_SRCS) add_executable(pb2tarscpp ${DIR_SRCS}) add_dependencies(pb2tarscpp thirdparty) target_link_libraries(pb2tarscpp ${LIB_PROTOBUF} ${LIB_PROTOC}) install(TARGETS "pb2tarscpp" RUNTIME DESTINATION tools) ``` -------------------------------- ### Make Single and Batch Async RPC Calls Source: https://context7.com/tarscloud/tarscpp/llms.txt Demonstrates how to initiate a single asynchronous RPC call and multiple batch asynchronous calls using Tars C++. Includes an example of hash routing for batch calls. Proper synchronization is required in real applications. ```cpp void asyncCallExample(HelloPrx prx) { int64_t startTime = TC_Common::now2us(); // Single async call HelloPrxCallbackPtr callback(new HelloCallback(startTime, 0)); prx->async_testHello(callback, "async request"); // Batch async calls for (int i = 0; i < 1000; i++) { HelloPrxCallbackPtr cb(new HelloCallback(TC_Common::now2us(), i)); // With hash routing prx->tars_hash(i)->async_testHello(cb, "batch_" + TC_Common::tostr(i)); } // Wait for all callbacks to complete // In real applications, use proper synchronization this_thread::sleep_for(chrono::seconds(5)); } ``` -------------------------------- ### Generate Clear Install Script Source: https://github.com/tarscloud/tarscpp/blob/master/CMakeLists.txt Creates a CMake script to remove the 'include' directory from the installation prefix. This is useful for ensuring a clean installation. ```cmake set(CLEAR_INCLUDE "clear-install.cmake") FILE(WRITE ${CLEAR_INCLUDE} "EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_INSTALL_PREFIX}/include)\n") install(SCRIPT ${CLEAR_INCLUDE}) ``` -------------------------------- ### Coroutine Example: Manual Scheduler Management Source: https://context7.com/tarscloud/tarscpp/llms.txt Manually create, initialize, run, and terminate a `TC_CoroutineScheduler`. This method provides fine-grained control over the coroutine lifecycle and resource allocation. ```cpp // Method 1: Manual scheduler management { // Create scheduler for current thread shared_ptr sched = TC_CoroutineScheduler::create(); // Initialize with memory pool (total size, stack size per coro) sched->init(10 * 1024 * 1024, 128 * 1024); // 10MB total, 128KB stack // Spawn coroutines using go() for (int i = 0; i < 10; i++) { sched->go(bind(coroTask, i)); } // Run scheduler (processes all coroutines) sched->run(); // Terminate when done sched->terminate(); } ``` -------------------------------- ### CMake Project Setup for Unit Tests Source: https://github.com/tarscloud/tarscpp/blob/master/unit-test/CMakeLists.txt Configures the CMake project for unit tests, including include and link directories for TarsCpp framework and gtest. Ensure necessary libraries are available in the specified paths. ```cmake project(unit-test) include_directories(${servant_SOURCE_DIR}/protocol/framework) include_directories(${servant_SOURCE_DIR}/protocol/servant) include_directories(${CMAKE_BINARY_DIR}/src/gtest/include) link_directories(${CMAKE_BINARY_DIR}/src/gtest/lib) link_directories(${CMAKE_BINARY_DIR}/src/gtest/lib64) include_directories(./) include_directories(../) # set(ENABLE_SHARED OFF) build_tars_server("unit-test" "") add_definitions(-DCMAKE_SOURCE_DIR="${PROJECT_SOURCE_DIR}") target_link_libraries(unit-test ${LIB_GTEST} tarsmock) if(TARS_MYSQL) if(UNIX) target_link_libraries(unit-test ${LIB_MYSQL}.a) else() target_link_libraries(unit-test ${LIB_MYSQL}.lib) endif() endif() add_dependencies(unit-test FRAMEWORK-PROTOCOL tarsmock) ``` -------------------------------- ### Define and Run Simple Coroutine Tasks Source: https://context7.com/tarscloud/tarscpp/llms.txt Defines a coroutine task function that starts, yields, sleeps, and resumes. Use `go()` to spawn coroutines and `yield()` or `sleep()` to manage their execution. ```cpp #include "util/tc_coroutine.h" #include "util/tc_thread.h" using namespace tars; // Simple coroutine function void coroTask(int id) { cout << "Coroutine " << id << " started" << endl; // Yield to other coroutines TC_CoroutineScheduler::scheduler()->yield(); cout << "Coroutine " << id << " resumed" << endl; // Sleep for specified milliseconds TC_CoroutineScheduler::scheduler()->sleep(100); cout << "Coroutine " << id << " completed" << endl; } ``` -------------------------------- ### Tars Server Application Class Source: https://context7.com/tarscloud/tarscpp/llms.txt The base class for Tars servers. Override initialize() to register servants and destroyApp() for cleanup. The main() function starts the server and waitForShutdown() keeps it running. ```cpp // HelloServer.h #include "servant/Application.h" class HelloServer : public tars::Application { public: virtual void initialize() override; virtual void destroyApp() override; }; ``` ```cpp // HelloServer.cpp #include "HelloServer.h" #include "HelloImp.h" HelloServer g_app; void HelloServer::initialize() { // Register servant implementation with object name addServant(ServerConfig::Application + "." + ServerConfig::ServerName + ".HelloObj"); // Optional: Add custom admin commands TARS_ADD_ADMIN_CMD_NORMAL("status", HelloServer::cmdStatus); } void HelloServer::destroyApp() { // Cleanup resources } bool HelloServer::cmdStatus(const string& command, const string& params, string& result) { result = "Server is running"; return true; } int main(int argc, char* argv[]) { try { g_app.main(argc, argv); g_app.waitForShutdown(); } catch (std::exception& e) { cerr << "Exception: " << e.what() << endl; } return 0; } ``` -------------------------------- ### Add Subdirectories for Build Source: https://github.com/tarscloud/tarscpp/blob/master/CMakeLists.txt Adds subdirectories to the CMake build process, including 'util', 'tools', 'servant', and 'mock'. It conditionally adds 'examples' and 'unit-test' if ONLY_LIB is not set. ```cmake add_subdirectory(util) add_subdirectory(tools) add_subdirectory(servant) add_subdirectory(mock) ``` ```cmake IF (NOT ${ONLY_LIB}) add_subdirectory(examples) add_subdirectory(unit-test) ENDIF() ``` -------------------------------- ### Coroutine Example: TC_Coroutine Class Usage Source: https://context7.com/tarscloud/tarscpp/llms.txt Utilizes the `TC_Coroutine` class to manage coroutines within a thread. Configure coroutine parameters like min/max count and stack size, then start and terminate the coroutine thread. ```cpp // Method 2: TC_Coroutine class { MyCoroutine coro; // Set coroutine parameters: // - min coroutines: 5 // - max coroutines: 10 // - stack size: 128KB coro.setCoroInfo(5, 10, 128 * 1024); // Start thread with coroutines coro.start(); // Wait for completion coro.terminate(); coro.getThreadControl().join(); } ``` -------------------------------- ### Implement Coroutine using TC_Coroutine Class Source: https://context7.com/tarscloud/tarscpp/llms.txt Inherit from `TC_Coroutine` to implement coroutine logic within the `handle` method. This class manages coroutines within a thread, requiring setup of coroutine info and starting the thread. ```cpp // Using TC_Coroutine class (thread + coroutines) class MyCoroutine : public TC_Coroutine { public: void handle(uint32_t coroId) override { cout << "Coroutine handle, id: " << coroId << endl; // Do work in coroutine context for (int i = 0; i < 5; i++) { cout << "Coro " << coroId << " iteration " << i << endl; TC_CoroutineScheduler::scheduler()->yield(); } } }; ``` -------------------------------- ### Tars IDL Definition Example Source: https://context7.com/tarscloud/tarscpp/llms.txt Defines a service interface 'Hello' with methods for testing, string manipulation, integer operations, and struct handling. Also defines a 'UserInfo' structure with name, age, and an optional email field. ```tars // Hello.tars - Service interface definition module TestApp { interface Hello { int test(); int testHello(string sReq, out string sRsp); int testInt(int iIn, out int iOut); int testStruct(UserInfo user, out UserInfo result); }; // Define custom structures struct UserInfo { 0 require string name; 1 require int age; 2 optional string email; }; }; ``` -------------------------------- ### Initialize and Configure Tars Communicator Source: https://context7.com/tarscloud/tarscpp/llms.txt Demonstrates creating a Communicator instance, setting properties like queue limits and network threads, and creating a service proxy using stringToProxy. Includes configuration for direct connections and service discovery via registry. ```cpp #include "servant/Communicator.h" #include "Hello.h" // Generated proxy using namespace tars; using namespace TestApp; int main() { // Create communicator Communicator comm; // Configure communicator properties comm.setProperty("sendqueuelimit", "1000000"); comm.setProperty("asyncqueuecap", "1000000"); comm.setProperty("netthread", "2"); // Create proxy - direct connection string obj = "TestApp.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 8999"; HelloPrx prx = comm.stringToProxy(obj); // Configure proxy timeouts prx->tars_connect_timeout(5000); // Connection timeout: 5s prx->tars_timeout(3000); // Sync call timeout: 3s prx->tars_async_timeout(60000); // Async call timeout: 60s // Test connection prx->tars_ping(); // Make synchronous call string response; int ret = prx->testHello("World", response); cout << "Response: " << response << ", ret: " << ret << endl; // Multiple endpoints for load balancing string multiObj = "TestApp.HelloServer.HelloObj@tcp -h 10.0.0.1 -p 8999:" "tcp -h 10.0.0.2 -p 8999:tcp -h 10.0.0.3 -p 8999"; HelloPrx lbPrx = comm.stringToProxy(multiObj); // Using registry for service discovery TC_Config conf; conf.parseFile("config.conf"); Communicator regComm(conf, "/tars/application/client"); HelloPrx regPrx = regComm.stringToProxy("TestApp.HelloServer.HelloObj"); return 0; } ``` -------------------------------- ### Configure and Make Synchronous HTTP Client Call Source: https://context7.com/tarscloud/tarscpp/llms.txt Use Communicator to create a proxy, configure it for HTTP/1.1 with connection pooling, and then make a synchronous HTTP call using http_call. Ensure proper timeouts are set. ```cpp #include "servant/Communicator.h" void httpClientExample() { Communicator comm; comm.setProperty("netthread", "2"); string httpObj = "TestApp.HttpServer.HttpObj@tcp -h 127.0.0.1 -p 8081"; ServantPrx prx = comm.stringToProxy(httpObj); // Configure for HTTP protocol with connection pooling (3 connections) prx->tars_set_protocol(ServantProxy::PROTOCOL_HTTP1, 3); prx->tars_connect_timeout(5000); prx->tars_async_timeout(60000); // Synchronous HTTP call shared_ptr req = make_shared(); req->setPostRequest("http://example.com/api/hello", "{\"name\":\"World\"}", true); req->setHeader("Content-Type", "application/json"); shared_ptr rsp; prx->http_call("hello", req, rsp); cout << "Status: " << rsp->getStatus() << endl; cout << "Body: " << rsp->getContent() << endl; // Async HTTP callback class HttpCallback : public tars::HttpCallback { public: virtual int onHttpResponse(const shared_ptr& rsp) override { cout << "Async response: " << rsp->getContent() << endl; return 0; } virtual int onHttpResponseException(int expCode) override { cout << "HTTP exception: " << expCode << endl; return 0; } }; HttpCallbackPtr cb = new HttpCallback(); prx->http_call_async("hello", req, cb); } ``` -------------------------------- ### HTTP Request and Response Handling in C++ Source: https://context7.com/tarscloud/tarscpp/llms.txt Demonstrates building HTTP requests, executing them via TC_HttpRequest, parsing raw HTTP responses, and utilizing TC_URL for URL component extraction. ```cpp #include "util/tc_http.h" #include "util/tc_clientsocket.h" using namespace tars; void httpUtilExample() { // ========== Building HTTP Requests ========== TC_HttpRequest req; // GET request req.setGetRequest("http://api.example.com/users?page=1"); // POST request with body req.setPostRequest("http://api.example.com/users", "{\"name\":\"John\",\"age\":30}", true); // true = keep content-length // Set headers req.setHeader("Content-Type", "application/json"); req.setHeader("Authorization", "Bearer token123"); req.setHeader("User-Agent", "TarsClient/1.0"); // Set cookies req.setCookie("session", "abc123"); // PUT/DELETE req.setPutRequest("http://api.example.com/users/1", "{\"name\":\"Jane\"}", true); req.setDeleteRequest("http://api.example.com/users/1", map(), true); // ========== Making HTTP Request ========== TC_HttpResponse rsp; int ret = req.doRequest(rsp, 5000); // 5s timeout if (ret == 0) { cout << "Status: " << rsp.getStatus() << endl; cout << "Headers:" << endl; TC_Http::http_header_type headers = rsp.getHeaders(); for (auto& h : headers) { cout << " " << h.first << ": " << h.second << endl; } cout << "Body: " << rsp.getContent() << endl; // Check specific header string contentType = rsp.getHeader("Content-Type"); } else { cout << "Request failed: " << ret << endl; } // ========== Using TC_TCPClient ========== TC_TCPClient client; client.init("api.example.com", 80, 3000); TC_HttpRequest req2; req2.setGetRequest("http://api.example.com/status"); TC_HttpResponse rsp2; ret = req2.doRequest(client, rsp2); // ========== Parsing HTTP Response ========== string rawHttp = "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 5\r\n" "\r\n" "Hello"; TC_HttpResponse parsed; parsed.decode(rawHttp); cout << "Parsed status: " << parsed.getStatus() << endl; cout << "Parsed body: " << parsed.getContent() << endl; // ========== URL Parsing ========== TC_URL url; url.parseURL("https://user:pass@example.com:8080/path?q=1#ref"); cout << "Scheme: " << url.getScheme() << endl; // https cout << "Host: " << url.getDomain() << endl; // example.com cout << "Port: " << url.getPort() << endl; // 8080 cout << "Path: " << url.getPath() << endl; // /path cout << "Query: " << url.getQuery() << endl; // q=1 cout << "User: " << url.getUser() << endl; // user } ``` -------------------------------- ### Perform Synchronous RPC Calls with Tars Source: https://context7.com/tarscloud/tarscpp/llms.txt Illustrates making synchronous RPC calls using generated proxies, including handling responses, errors, and exceptions. Shows how to apply hash routing, consistent hashing, per-call timeout overrides, and batch calls across multiple threads. ```cpp #include "servant/Communicator.h" #include "Hello.h" using namespace tars; using namespace TestApp; void syncCallExample(HelloPrx prx) { try { // Simple synchronous call string request = "Hello World"; string response; int ret = prx->testHello(request, response); if (ret == 0) { cout << "Success: " << response << endl; } else { cout << "Service returned error: " << ret << endl; } // Call with hash routing (same key goes to same server) for (int i = 0; i < 100; i++) { string key = "user_" + TC_Common::tostr(i % 10); prx->tars_hash(TC_Common::hashNew(key))->testHello(key, response); } // Per-call timeout override prx->tars_set_timeout(1000)->testHello("quick", response); // Consistent hash for better distribution during scaling prx->tars_consistent_hash(userId)->testHello(request, response); } catch (TarsException& e) { cerr << "Tars exception: " << e.what() << endl; } catch (std::exception& e) { cerr << "Exception: " << e.what() << endl; } } ``` ```cpp // Batch synchronous calls with multiple threads void multiThreadSync(HelloPrx prx, int threadCount, int callCount) { vector threads; atomic successCount(0); for (int t = 0; t < threadCount; t++) { threads.push_back(new thread([&, prx]() { for (int i = 0; i < callCount; i++) { string response; if (prx->testHello("test", response) == 0) { successCount++; } } })); } for (auto t : threads) { t->join(); delete t; } cout << "Success: " << successCount << "/" << threadCount * callCount << endl; } ``` -------------------------------- ### MySQL Database Operations with TC_Mysql Source: https://context7.com/tarscloud/tarscpp/llms.txt Demonstrates connecting to a MySQL database, executing queries, handling results, performing inserts with escaping, managing transactions, and using prepared statements. Ensure TC_Mysql is properly configured and imported. ```cpp #include "util/tc_mysql.h" using namespace tars; void mysqlExample() { // Database configuration TC_DBConf dbConf; dbConf._host = "127.0.0.1"; dbConf._port = 3306; dbConf._user = "root"; dbConf._password = "password"; dbConf._database = "testdb"; dbConf._charset = "utf8mb4"; TC_Mysql mysql; try { // Connect to database mysql.init(dbConf); mysql.connect(); // Execute simple query TC_Mysql::MysqlData data = mysql.queryRecord( "SELECT id, name, age FROM users WHERE status = 1"); // Iterate results for (size_t i = 0; i < data.size(); i++) { cout << "ID: " << data[i]["id"] << ", Name: " << data[i]["name"] << ", Age: " << data[i]["age"] << endl; } // Insert with escaping string name = "John's \"Quote\""; string safeName = mysql.escapeString(name); string sql = "INSERT INTO users (name, age, created_at) VALUES ('" + safeName + "', 25, NOW())"; mysql.execute(sql); // Get last insert ID long lastId = mysql.lastInsertID(); cout << "Inserted ID: " << lastId << endl; // Update mysql.execute("UPDATE users SET age = 26 WHERE id = " + TC_Common::tostr(lastId)); // Affected rows size_t affected = mysql.getAffectedRows(); cout << "Updated rows: " << affected << endl; // Transaction mysql.execute("START TRANSACTION"); try { mysql.execute("INSERT INTO orders (user_id, amount) VALUES (1, 100)"); mysql.execute("UPDATE accounts SET balance = balance - 100 WHERE user_id = 1"); mysql.execute("COMMIT"); } catch (TC_Mysql_Exception& e) { mysql.execute("ROLLBACK"); throw; } // Prepared statement style (using escaping) map> columns; columns["name"] = make_pair(TC_Mysql::DB_STR, "Alice"); columns["age"] = make_pair(TC_Mysql::DB_INT, "30"); columns["score"] = make_pair(TC_Mysql::DB_INT, "95.5"); mysql.insertRecord("users", columns); // Batch record retrieval with condition string condition = "age > 20 ORDER BY id LIMIT 10"; data = mysql.queryRecord("SELECT * FROM users WHERE " + condition); } catch (TC_Mysql_Exception& e) { cerr << "MySQL error: " << e.what() << endl; } // Disconnect mysql.disconnect(); } ``` -------------------------------- ### Project Initialization and Policy Source: https://github.com/tarscloud/tarscpp/blob/master/CMakeLists.txt Initializes the CMake project and sets the policy for CMP0135 to NEW if it exists. This ensures modern CMake behavior. ```cmake cmake_minimum_required(VERSION 3.16) project(tars-cpp) ``` ```cmake if(EXISTS CMP0135) cmake_policy(SET CMP0135 NEW) endif() ``` -------------------------------- ### Initialize and Update Unittest Submodule Source: https://github.com/tarscloud/tarscpp/blob/master/README.md Commands to initialize and update the GoogleTest-based unit testing submodule. ```bash git submodule init unittest;git submodule update ``` -------------------------------- ### JSON Creation, Parsing, and Error Handling in C++ Source: https://context7.com/tarscloud/tarscpp/llms.txt Demonstrates how to construct complex JSON structures, parse raw JSON strings into objects, and handle potential parsing exceptions. ```cpp #include "util/tc_json.h" using namespace tars; void jsonExample() { // ========== Creating JSON ========== // Create JSON object JsonValueObjPtr root = new JsonValueObj(); // Add string root->value["name"] = new JsonValueString("John Doe"); // Add number root->value["age"] = new JsonValueNum(30, false); // false = integer root->value["score"] = new JsonValueNum(95.5, true); // true = double // Add boolean root->value["active"] = new JsonValueBoolean(true); // Add null root->value["nickname"] = new JsonValueNull(); // Add nested object JsonValueObjPtr address = new JsonValueObj(); address->value["city"] = new JsonValueString("New York"); address->value["zip"] = new JsonValueString("10001"); root->value["address"] = address; // Add array JsonValueArrayPtr tags = new JsonValueArray(); tags->value.push_back(new JsonValueString("developer")); tags->value.push_back(new JsonValueString("engineer")); tags->value.push_back(new JsonValueNum(2024, false)); root->value["tags"] = tags; // Convert to JSON string string jsonStr = TC_Json::writeValue(root); cout << "Generated JSON: " << jsonStr << endl; // {"name":"John Doe","age":30,"score":95.5,"active":true,...} // ========== Parsing JSON ========== string input = R"({ "users": [ {"id": 1, "name": "Alice", "email": "alice@example.com"}, {"id": 2, "name": "Bob", "email": "bob@example.com"} ], "total": 2, "page": 1 })"; JsonValuePtr parsed = TC_Json::getValue(input); // Access as object JsonValueObjPtr obj = JsonValueObjPtr::dynamicCast(parsed); if (obj) { // Get number JsonValueNumPtr total = JsonValueNumPtr::dynamicCast(obj->value["total"]); cout << "Total: " << total->value << endl; // Get array JsonValueArrayPtr users = JsonValueArrayPtr::dynamicCast(obj->value["users"]); for (auto& item : users->value) { JsonValueObjPtr user = JsonValueObjPtr::dynamicCast(item); JsonValueNumPtr id = JsonValueNumPtr::dynamicCast(user->value["id"]); JsonValueStringPtr name = JsonValueStringPtr::dynamicCast(user->value["name"]); cout << "User " << (int64_t)id->value << ": " << name->value << endl; } } // ========== Error Handling ========== try { string badJson = "{invalid json}"; JsonValuePtr bad = TC_Json::getValue(badJson); } catch (TC_Json_Exception& e) { cerr << "JSON parse error: " << e.what() << endl; } } ``` -------------------------------- ### Client-Side Push Notification Receiver (C++) Source: https://context7.com/tarscloud/tarscpp/llms.txt Sets up a client to receive server-initiated push messages. Requires implementing a callback class derived from `PushPrxCallback` to handle incoming notifications. The `tars_set_push_callback()` method is used to register this callback. ```cpp // ============= PUSH CLIENT ============= #include "servant/Communicator.h" #include "Push.h" // Push callback receives server-initiated messages class PushCallback : public PushPrxCallback { public: virtual void callback_cycleNotify(int ret, const PushInfo& info) override { cout << "Push received: " << info.message << " at " << info.timestamp << endl; } virtual void onConnect(const TC_Endpoint& ep) override { cout << "Connected to: " << ep.toString() << endl; } virtual void onClose() override { cout << "Connection closed" << endl; } }; void pushClientExample() { Communicator comm; string pushObj = "TestApp.PushServer.PushObj@tcp -h 127.0.0.1 -p 9000"; PushPrx prx = comm.stringToProxy(pushObj); // Register push callback PushPrxCallbackPtr pushCb(new PushCallback()); prx->tars_set_push_callback(pushCb); // Register with server prx->registerClient("client_001"); // Keep connection alive and receive pushes while (true) { this_thread::sleep_for(chrono::seconds(1)); } } ``` -------------------------------- ### Server-Side Push Implementation (C++) Source: https://context7.com/tarscloud/tarscpp/llms.txt Implements the server logic for handling client connections and pushing messages. Stores client connections and provides methods to register clients and push messages. Ensure `current->setResponse(false)` is used to keep the connection alive for pushes. ```cpp // ============= PUSH SERVER ============= // PushImp.cpp - Server pushes to connected clients class PushImp : public TestApp::Push { public: // Store client connections map _clients; TC_ThreadMutex _mutex; virtual int registerClient(const string& clientId, TarsCurrentPtr current) override { TC_LockT lock(_mutex); _clients[clientId] = current; // Keep connection alive for push current->setResponse(false); // Don't auto-respond return 0; } // Push message to specific client void pushToClient(const string& clientId, const string& message) { TC_LockT lock(_mutex); auto it = _clients.find(clientId); if (it != _clients.end()) { TarsCurrentPtr current = it->second; // Build push response TestApp::PushInfo info; info.message = message; info.timestamp = TC_Common::now2str(); vector buffer; // Encode and send current->sendResponse(0, buffer); } } virtual int doClose(TarsCurrentPtr current) override { // Client disconnected, clean up TC_LockT lock(_mutex); for (auto it = _clients.begin(); it != _clients.end(); ) { if (it->second->getUId() == current->getUId()) { it = _clients.erase(it); } else { ++it; } } return 0; } }; ``` -------------------------------- ### Register HTTP Server Servant and Protocol Source: https://context7.com/tarscloud/tarscpp/llms.txt In your HttpServer's initialize method, register your HttpImp servant and specify TC_NetWorkBuffer::parseHttp as the protocol parser to enable HTTP handling. ```cpp // HttpServer.cpp - Register with HTTP protocol void HttpServer::initialize() { addServant(ServerConfig::Application + "." + ServerConfig::ServerName + ".HttpObj"); // Set HTTP protocol parser addServantProtocol(ServerConfig::Application + "." + ServerConfig::ServerName + ".HttpObj", &TC_NetWorkBuffer::parseHttp); } ``` -------------------------------- ### Include Project Directories Source: https://github.com/tarscloud/tarscpp/blob/master/servant/script/cmake_http_demo/CMakeLists.txt Includes necessary directories for the project. Adjust paths as needed for your project structure and third-party libraries. ```cmake include_directories(/usr/local/tars/cpp/thirdparty/include) # link_directories(/usr/local/tars/cpp/thirdparty/lib) #include_directories(/home/tarsprotol/App/OtherServer) add_subdirectory(src) ``` -------------------------------- ### Configure IDL and Protocol Settings Source: https://github.com/tarscloud/tarscpp/blob/master/tools/tars2node/CMakeLists.txt Sets paths for RPC and stream modules, and defines the IDL type and protocol name. ```cmake set(RPC_MODULE_PATH "@tars/rpc") set(STREAM_MODULE_PATH "@tars/stream") set(IDL_TYPE "Tars") set(PROTOCOL_NAME "Tup") ``` -------------------------------- ### Add Build Dependencies in CMake Source: https://github.com/tarscloud/tarscpp/blob/master/examples/UtilDemo/demo-server/CMakeLists.txt Ensures that the 'tarsutil' library is built before the 'demo-server' executable. ```cmake add_dependencies(demo-server tarsutil) ``` -------------------------------- ### Basic CMake Configuration for TarsCpp Server Source: https://github.com/tarscloud/tarscpp/blob/master/servant/script/cmake_demo/src/CMakeLists.txt This snippet shows the minimal CMake configuration required to build a TarsCpp server. It sets the minimum CMake version, defines the project name, and uses the `gen_server` macro to generate the server code. ```cmake cmake_minimum_required(VERSION 3.16) project(DemoApp-DemoServer) gen_server(DemoApp DemoServer) ``` -------------------------------- ### Add Preprocessor Definitions Source: https://github.com/tarscloud/tarscpp/blob/master/tools/tars2node/CMakeLists.txt Defines preprocessor macros for the build, including version information, namespaces, paths, and execution filename. ```cmake add_definitions(-DPARSER_VERSION="${PARSER_VERSION}") add_definitions(-DGENERATOR_VERSION="${GENERATOR_VERSION}") add_definitions(-DTC_NAMESPACE=${TC_NAMESPACE}) add_definitions(-DIDL_NAMESPACE=${IDL_NAMESPACE}) add_definitions(-DGRAMMAR_NAME=${GRAMMAR_NAME}) add_definitions(-DRPC_MODULE_PATH="${RPC_MODULE_PATH}") add_definitions(-DSTREAM_MODULE_PATH="${STREAM_MODULE_PATH}") add_definitions(-DEXECUTE_FILENAME="${PROJECT_NAME}") add_definitions(-DIDL_TYPE="${IDL_TYPE}") add_definitions(-DPROTOCOL_NAME="${PROTOCOL_NAME}") ``` -------------------------------- ### Implement Async Callback for Tars RPC Source: https://context7.com/tarscloud/tarscpp/llms.txt Create a callback class inheriting from the generated PrxCallback class and implement callback_xxx() and callback_xxx_exception() methods to handle asynchronous RPC responses. ```cpp #include "servant/Communicator.h" #include "Hello.h" using namespace tars; using namespace TestApp; // Async callback implementation class HelloCallback : public HelloPrxCallback { public: HelloCallback(int64_t startTime, int index) : _startTime(startTime), _index(index) {} // Success callback virtual void callback_testHello(int ret, const string& response) override { int64_t cost = TC_Common::now2us() - _startTime; cout << "Async callback[" << _index << "]: ret=" << ret << ", response=" << response << ", cost=" << cost << "us" << endl; } // Exception callback virtual void callback_testHello_exception(tars::Int32 ret) override { cout << "Async exception[" << _index << "]: error code=" << ret << endl; // Error codes: TARSSERVERSUCCESS, TARSSERVERDECODEERR, // TARSSERVERENCODEERR, TARSSERVERNOFUNCERR, etc. } private: int64_t _startTime; int _index; }; ``` -------------------------------- ### Define Lexer Generation Macro (CMake) Source: https://github.com/tarscloud/tarscpp/blob/master/tools/tarsgrammar/CMakeLists.txt This macro, `complice_lex`, finds all .l files in the current source directory, uses `flex` to generate .lex.cpp files, and collects them in a list. It's used to automate the lexer compilation process within the CMake build. ```cmake macro(complice_lex OUT_DEPENDS_LEX_LIST) set(DEPENDS_LIST) FILE(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/*.l") foreach (FILE ${SRC_LIST}) get_filename_component(NAME_WE ${FILE} NAME_WE) set(LEX_IN ${FILE}) set(LEX_CPP ${NAME_WE}.lex.cpp) add_custom_command( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${LEX_CPP} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${LEX_IN} COMMAND flex --nounist ${LEX_IN} COMMENT "flex --nounist ${LEX_IN}") list(APPEND DEPENDS_LIST ${CMAKE_CURRENT_SOURCE_DIR}/${LEX_CPP}) get_filename_component(PATH ${FILE} PATH) endforeach (FILE) set(OUT_DEPENDS_LEX_LIST ${DEPENDS_LIST}) endmacro() ``` -------------------------------- ### Tars Build Options Configuration Source: https://github.com/tarscloud/tarscpp/blob/master/servant/script/cmake_http_demo/CMakeLists.txt Configures build options for TARS features like MySQL, SSL, and HTTP2. Use 'ON' to enable, 'OFF' to disable. ```cmake option(TARS_MYSQL "option for mysql" ON) option(TARS_SSL "option for ssl" OFF) option(TARS_HTTP2 "option for http2" OFF) ``` -------------------------------- ### Create Executable in CMake Source: https://github.com/tarscloud/tarscpp/blob/master/examples/UtilDemo/demo-server/CMakeLists.txt Defines the main executable for the project using the collected source files. ```cmake add_executable(demo-server ${SRC_LIST}) ``` -------------------------------- ### Implement HTTP Server Request Handler Source: https://context7.com/tarscloud/tarscpp/llms.txt Override the doRequest method in your Servant class to handle incoming HTTP requests. This involves decoding the request, processing its components, and encoding a response. ```cpp // HttpImp.h #include "servant/Servant.h" class HttpImp : public tars::Servant { publicpublic: virtual void initialize() override; virtual void destroy() override; // Handle HTTP requests virtual int doRequest(tars::TarsCurrentPtr current, vector& buffer) override; }; // HttpImp.cpp int HttpImp::doRequest(tars::TarsCurrentPtr current, vector& buffer) { TC_HttpRequest request; request.decode(current->getRequestBuffer()); // Process request string method = request.getMethod(); string path = request.getRequest(); string body = request.getContent(); // Build response TC_HttpResponse response; response.setResponse(200, "OK", "Hello from Tars HTTP Server"); response.setHeader("Content-Type", "application/json"); response.setHeader("Connection", request.getHeader("Connection")); response.encode(buffer); return 0; } ``` -------------------------------- ### Include Project Directories Source: https://github.com/tarscloud/tarscpp/blob/master/CMakeLists.txt Adds necessary include directories for the project's utility and servant modules. It conditionally adds a directory for epoll on Windows. ```cmake include_directories(${PROJECT_SOURCE_DIR}/util/include) include_directories(${PROJECT_SOURCE_DIR}/servant) include_directories(${PROJECT_SOURCE_DIR}/servant/protocol) ``` ```cmake IF(WIN32) include_directories(${PROJECT_SOURCE_DIR}/util/src/epoll_windows) ENDIF() ``` -------------------------------- ### Linking External Libraries in CMake Source: https://github.com/tarscloud/tarscpp/blob/master/servant/script/cmake_demo/src/CMakeLists.txt This commented-out line demonstrates how to link external libraries such as mysqlclient, ssl, crypto, and nghttp2 to your TarsCpp project. Uncomment and modify as needed for your project's dependencies. ```cmake #target_link_libraries(mysqlclient ssl crypto nghttp2-static) ``` -------------------------------- ### Configure Tool Subdirectories Source: https://github.com/tarscloud/tarscpp/blob/master/tools/CMakeLists.txt Adds subdirectories for various Tars language generators and grammar tools to the build process. ```cmake IF(UNIX) add_subdirectory(tarsgrammar) ENDIF(UNIX) add_subdirectory(tarsparse) add_subdirectory(tarsmerge) add_subdirectory(tars2cpp) add_subdirectory(tars2python) add_subdirectory(tars2cs) add_subdirectory(tars2c) add_subdirectory(tars2oc) add_subdirectory(tars2php) add_subdirectory(tars2android) add_subdirectory(tars2node) add_subdirectory(tars2case) IF(TARS_PROTOBUF) add_subdirectory(pb2tarscpp) ENDIF() ``` -------------------------------- ### Include Directories in CMake Source: https://github.com/tarscloud/tarscpp/blob/master/examples/UtilDemo/demo-server/CMakeLists.txt Specifies include directories for the project. Ensure the path is correct relative to the build system. ```cmake include_directories(${util_SOURCE_DIR}/include) ``` -------------------------------- ### Configure Tars Server via XML Source: https://context7.com/tarscloud/tarscpp/llms.txt Defines client RPC settings, server paths, and adapter endpoints for a Tars service. ```xml locator = tars.tarsregistry.QueryObj@tcp -h 127.0.0.1 -p 17890 sync-invoke-timeout = 5000 async-invoke-timeout = 60000 refresh-endpoint-interval = 10000 stat = tars.tarsstat.StatObj sendqueuelimit = 100000 asyncqueuecap = 100000 asyncthread = 3 netthread = 2 modulename = TestApp.HelloServer closecout = 0 app = TestApp server = HelloServer basepath = /usr/local/app/tars/tarsnode/data/TestApp.HelloServer/bin/ datapath = /usr/local/app/tars/tarsnode/data/TestApp.HelloServer/data/ logpath = /usr/local/app/tars/app_log/ netthread = 4 opencoroutine = 0 local = tcp -h 127.0.0.1 -p 15001 -t 10000 node = tars.tarsnode.ServerObj@tcp -h 127.0.0.1 -p 19386 config = tars.tarsconfig.ConfigObj notify = tars.tarsnotify.NotifyObj log = tars.tarslog.LogObj endpoint = tcp -h 0.0.0.0 -p 8999 -t 60000 allow = maxconns = 20000 threads = 5 servant = TestApp.HelloServer.HelloObj queuecap = 1000000 protocol = tars endpoint = tcp -h 0.0.0.0 -p 8080 -t 60000 threads = 3 servant = TestApp.HelloServer.HttpObj protocol = not_tars queuecap = 100000 ```