Context
I have been working with C++ for about the past 5-6 months and I'm beginning to learn gRPC. I have followed many tutorials online to get started, but I want to build a client-server communication app from scratch. Probably a bit too much, but I'm doing my best to understand how to get it all to work from the ground up rather than downloading, typing 'make', and then having a working product that I don't know how to implement into my own projects.
Goal: Create and run a simple C++ gRPC client-server communication
Versions
Using VSCode IDE.
Protoc = libprotoc 3.17.3
gRPC = 1.41.1
make = 3.81
Files
mathtest.proto
syntax = "proto3";
option java_package = "ex.grpc";
package mathtest;
// Defines the service
service MathTest {
// Function invoked to send the request
rpc sendRequest (MathRequest) returns (MathReply) {}
}
// The request message containing requested numbers
message MathRequest {
int32 a = 1;
int32 b = 2;
}
// The response message containing response
message MathReply {
int32 result = 1;
}
server.cpp
#include <string>
#include <grpcpp/grpcpp.h>
#include "mathtest.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using mathtest::MathTest;
using mathtest::MathRequest;
using mathtest::MathReply;
class MathServiceImplementation final : public MathTest::Service {
Status sendRequest(
ServerContext* context,
const MathRequest* request,
MathReply* reply
) override {
int a = request->a();
int b = request->b();
reply->set_result(a * b);
return Status::OK;
}
};
void Run() {
std::string address("0.0.0.0:5000");
MathServiceImplementation service;
ServerBuilder builder;
builder.AddListeningPort(address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on port: " << address << std::endl;
server->Wait();
}
int main(int argc, char** argv) {
Run();
return 0;
}
client.cpp
#include <string>
#include <grpcpp/grpcpp.h>
#include "mathtest.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using mathtest::MathTest;
using mathtest::MathRequest;
using mathtest::MathReply;
class MathTestClient {
public:
MathTestClient(std::shared_ptr<Channel> channel) : stub_(MathTest::NewStub(channel)) {}
int sendRequest(int a, int b) {
MathRequest request;
request.set_a(a);
request.set_b(b);
MathReply reply;
ClientContext context;
Status status = stub_->sendRequest(&context, request, &reply);
if(status.ok()){
return reply.result();
} else {
std::cout << status.error_code() << ": " << status.error_message() << std::endl;
return -1;
}
}
private:
std::unique_ptr<MathTest::Stub> stub_;
};
void Run() {
std::string address("0.0.0.0:5000");
MathTestClient client(
grpc::CreateChannel(
address,
grpc::InsecureChannelCredentials()
)
);
int response;
int a = 5;
int b = 10;
response = client.sendRequest(a, b);
std::cout << "Answer received: " << a << " * " << b << " = " << response << std::endl;
}
int main(int argc, char* argv[]){
Run();
return 0;
}
Steps taken for compilation
Use mathtest.proto to create the necessary files via 'protoc' (or protobuf) by executing these: protoc --grpc_out=. --plugin=protoc-gen-grpc=/opt/homebrew/bin/grpc_cpp_plugin mathtest.proto & protoc --cpp_out=. mathtest.proto
This creates the following files:
mathtest.pb.h
mathtest.pb.cc
mathtest.grpc.pb.h
mathtest.grpc.pb.cc
Compile client.cpp & server.cpp files to create executable binaries using these commands: g++ -std=c++17 client.cpp mathtest.pb.cc mathtest.grpc.pb.cc -o client 'pkg-config --libs protobuf grpc++' (NOTE: in this post, I use a single quote in the command line, but in the actual command I use a backtick; just wanted to make that clear)
Errors
As you may notice, I can't get to compiling the server because I can't get past the client compilation first. After executing the above command in step 2 of compilation, this is my output:
g++ -std=c++17 client.cpp mathtest.pb.cc mathtest.grpc.pb.cc -o client `pkg-config --libs protobuf grpc++`
client.cpp:4:10: fatal error: 'grpcpp/grpcpp.h' file not found
#include <grpcpp/grpcpp.h>
^~~~~~~~~~~~~~~~~
1 error generated.
In file included from mathtest.pb.cc:4:
./mathtest.pb.h:10:10: fatal error: 'google/protobuf/port_def.inc' file not found
#include <google/protobuf/port_def.inc>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
In file included from mathtest.grpc.pb.cc:5:
./mathtest.pb.h:10:10: fatal error: 'google/protobuf/port_def.inc' file not found
#include <google/protobuf/port_def.inc>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
make: *** [client] Error 1
Here's my real confusion...
C++ intellisense has no issues finding these files. My $PATH variables point to these folders, and my VS Code include path also point to these folders. I'm unsure where I am going wrong here...
echo $PATH returns this:
/opt/homebrew/bin:/opt/homebrew/sbin:/opt/homebrew/include:/opt/homebrew/Cellar:/opt/homebrew/opt/libtool/libexec/gnubin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/tzeller/.local/bin
The folders in question ('google' & 'grcpp') live within /opt/homebrew/include and they hold the necessary files as well...
What am I missing??
Change your compile command to
g++ -std=c++17 client.cpp mathtest.pb.cc mathtest.grpc.pb.cc -o client `pkg-config --libs --cflags protobuf grpc++`
The --cflags bit asks pkg-config to spit out the necessary parameters for setting the header search path (on my system -I/opt/homebrew/Cellar/grpc/1.41.1/include and others)
i want to compile this small program:
#include <iostream>
#include <SDL.h>
using namespace std;
int main() {
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
cout << "SDL init failed" << endl;
return 1;
}
cout << "Hello World" << endl;
return 0;
}
which I can do successfully with:
g++ -o SDL_basic SDL_basic.cpp -I/opt/local/include/SDL2/ -l SDL2 -L/opt/local/lib/
I spent the last hour trying to modify the makefile I usually use to do the same:
CXX=g++
INC=-I/opt/local/include/SDL2/
LIBSEARCHPATH=-L/opt/local/lib/
LIBS=-l SDL2
OBJECTS=$(PROJECT).o
PROJECT=SDL_basic
$(PROJECT) : $(OBJECTS)
${CXX} $(CPFLAGS) $(OBJECTS) -o $# ${INC} ${LIBSEARCHPATH} ${LIBS}
.cpp.o :
${CXX} -c ${CPFLAGS} $? ${INC} ${LIBSEARCHPATH} ${LIBS}
clean :
rm -f $(OBJECTS)
Running make finally works now, but if I run it a second time after a small change in the .cpp, I get the following warning:
clang: warning: -lSDL2: 'linker' input unused [-Wunused-command-line-argument]
clang: warning: argument unused during compilation: '-L/opt/local/lib/' [-Wunused-command-line-argument]
Therefore my question. Does this make sense what I did in the makefile? Do the arguments ${INC} ${LIBSEARCHPATH} ${LIBS} really belong after both .cpp.o : and $(PROJECT) : $(OBJECTS) (Otherwise it does not seem to work)?
I hope somebody can help me.
Thank you a lot and best regards!!
F
I have a simple C++ program which I compile with clang using the Boost library and with C++14 support.
I use the following command to compile my sample.cpp file and it works fine:
clang++ -g -std=c++1y -I$BOOST_ROOT sample.cpp -o sample
where $BOOST_ROOT is the path to where I downloaded and extracted the boost zip file.
$BOOST_ROOT=/usr/local/boost_1_66_0/
When I try to compile the same sample.cpp file with a makefile, it doesn't work.
This is what my makefile looks like:
sample: sample.cpp
clang++ -g -std=c++1y -I$BOOST_ROOT sample.cpp -o sample
Running make command, I get the error:
Sample.cpp:9:10: fatal error: 'boost/format.hpp' file not found
#include <boost/format.hpp>
^~~~~~~~~~~~~~~~~~
1 error generated.
And here is the sample.cpp
//
// sample.cpp
//
//
#include <iostream>
#include <boost/format.hpp>
using namespace std;
int main()
{
std::cout << "Enter your first name: " << std::endl;
std::string firstName;
std::cin >> firstName;
std::cout << "Enter your surname: " << std::endl;
std::string surname;
std::cin >> surname;
auto formattedName = str( boost::format("%1% %2%"s) % firstName % surname );
std::cout << "You said your name is: " << formattedName << std::endl;
return 0;
}
CONSOLE OUTPUT
MacBook-Air:Listing_1_7 userd43f$ make
c++ sample.cpp -o sample
sample.cpp:9:10: fatal error: 'boost/format.hpp' file not found
#include <boost/format.hpp>
^~~~~~~~~~~~~~~~~~
1 error generated.
make: *** [sample] Error 1
MacBook-Air:Listing_1_7 userd43f$ ls $BOOST_ROOT
INSTALL boost boost.png bootstrap.sh index.html rst.css
Jamroot boost-build.jam boostcpp.jam doc libs status
LICENSE_1_0.txt boost.css bootstrap.bat index.htm more tools
The problem was that there were spaces instead of a tab in the command-line of my makefile.
BOOST_ROOT := /usr/local/boost_1_66_0
BOOST_INC := ${BOOST_ROOT}/include
sample: sample.cpp
clang++ -g -std=c++1y -I$(BOOST_ROOT) sample.cpp -o sample
In the last line just before clang++ command I was using spaces instead of a TAB. I replaced all the spaces with a single TAB before clang++, and also I needed to put the BOOST_ROOT inside the parentheses as (mentioned by #MaximEgorushkin)
Then it started picking up the right command, as shown in the output below:
MacBook-Air:Listing_1_7 userd43f$ make
clang++ -g -std=c++1y -I/usr/local/boost_1_66_0 sample.cpp -o sample
Boost root directory normally has include and lib directories in it.
It should probably be:
BOOST_ROOT := /usr/local/boost_1_66_0
BOOST_INC := ${BOOST_ROOT}/include
sample: sample.cpp
clang++ -g -std=c++1y -I${BOOST_INC} sample.cpp -o sample
I have installed boost and cpp-netlib and successfully ran all tests. I can compile the following example from the command line with the following options:
clang++ -o test main.cpp \
-I/path.../cpp-netlib-0.11.1-final \
-I/path.../boost_1_57_0 \
-L/path.../boost_1_57_0/stage/lib \
-L/usr/local/lib \
-lboost_system \
-lboost_thread \
-lcppnetlib-uri \
-lcppnetlib-client-connections \
-lssl \
-lcrypto \
-pthread
Example from cpp-netlib.org:
#include <boost/network/protocol/http/client.hpp>
#include <iostream>
int main(int argc, char *argv[]) {
using namespace boost::network;
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " [url]" << std::endl;
return 1;
}
http::client client;
http::client::request request(argv[1]);
request << header("Connection", "close");
http::client::response response = client.get(request);
std::cout << body(response) << std::endl;
return 0;
}
Running this program:
./test "url"
Successfully displays the html code of website.
I am running into a problem when I try to import this example into Xcode. I have included the correct search paths for header files and binaries. The code will compile but Xcode crashes every time it reaches this line.
http::client::request request(argv[1]);
I can comment out this line and everything after it and the program compiles and runs. Otherwise it crashes, even when trying to use a breakpoint. Any suggestions would be appreciated.
This is because of the URI parser generating really big symbols in debug builds. The use of Boost.Spirit in the URI parsing uses very heavy template metaprogramming and that shows up as very long symbols which may not be handled appropriately by the system. I'm not sure whether there are work-arounds here as I don't use the Xcode IDE for regular development of cpp-netlib.
$clang --version
Apple LLVM version 6.0 (clang-600.0.51) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix
magick_wrapper.cpp
#include <Magick++.h>
#include <iostream>
#include <stdexcept>
#include "magick_wrapper.h"
using namespace std;
using namespace Magick;
void hello_world() {
cout << "hello world" << endl;
}
void initialize_imagick() {
static int isInitialized = 0;
if(!isInitialized) {
InitializeMagick(NULL);
isInitialized = 1;
}
}
void throw_exception_test() {
try {
throw runtime_error("just a test");
} catch (exception &error) {
cout << "error occured: " << error.what() << endl;
} catch (...) {
cout << "an exception has rasie" << ", but we don't know what it is" << endl;
}
}
haskell code with ffi
{-# LANGUAGE ForeignFunctionInterface #-}
module Main where
import Foreign.C
import Foreign.Ptr
foreign import ccall "hello_world"
c_hello_world :: IO ()
foreign import ccall "throw_exception_test"
c_throw_exception_test :: IO ()
main = do
c_hello_world
c_throw_exception_test
main.cpp
#include <iostream>
#include "magick_wrapper.h"
using namespace std;
int main() {
cout << "main start" << endl;
throw_exception_test();
cout << "main ends" << endl;
return 0;
}
make file
CC=clang
all: test testc
testc: magick_wrapper.o main.o
$(CC) -o testc main.o magick_wrapper.o `Magick++-config --cppflags --cxxflags --ldflags --libs` -lc++
main.o: main.cpp
$(CC) -c main.cpp
test: test.hs magick_wrapper.o
ghc -v -o test test.hs magick_wrapper.o `Magick++-config --ldflags --libs` -lc++
magick_wrapper.o: magick_wrapper.cpp
$(CC) -c `Magick++-config --cppflags --cxxflags` magick_wrapper.cpp -stdlib=libc++
clean:
rm *o *hi test testc
std output from test (haskell ffi)
$ ./test
hello world
libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: just a test
Abort trap: 6
std ouptut from testc (pure c)
$ ./testc
main start
error occured: just a test
main ends
ghc verbose output
*** C Compiler:
clang -m64 -fno-stack-protector -DTABLES_NEXT_TO_CODE -c /var/folders/kp/f2t2ps216tl3p4t6jsplr9400000gn/T/ghc26839_0/ghc26839_4.c -o /var/folders/kp/f2t2ps216tl3p4t6jsplr9400000gn/T/ghc26839_0/ghc26839_5.o -I/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/include
*** Linker:
clang -m64 -fno-stack-protector -DTABLES_NEXT_TO_CODE -m64 -o test -Wl,-no_compact_unwind test.o -L/usr/local/Cellar/imagemagick/6.8.9-7/lib -L/usr/local/Cellar/imagemagick/6.8.9-7/lib magick_wrapper.o '-lMagick++-6.Q16' -lMagickWand-6.Q16 -lMagickCore-6.Q16 '-lMagick++-6.Q16' -lMagickWand-6.Q16 -lMagickCore-6.Q16 '-lc++' -L/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/base-4.7.0.1 -L/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/integer-gmp-0.5.1.0 -L/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/ghc-prim-0.3.1.0 -L/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/rts-1.0 /var/folders/kp/f2t2ps216tl3p4t6jsplr9400000gn/T/ghc26839_0/ghc26839_5.o -Wl,-u,_ghczmprim_GHCziTypes_Izh_static_info -Wl,-u,_ghczmprim_GHCziTypes_Czh_static_info -Wl,-u,_ghczmprim_GHCziTypes_Fzh_static_info -Wl,-u,_ghczmprim_GHCziTypes_Dzh_static_info -Wl,-u,_base_GHCziPtr_Ptr_static_info -Wl,-u,_ghczmprim_GHCziTypes_Wzh_static_info -Wl,-u,_base_GHCziInt_I8zh_static_info -Wl,-u,_base_GHCziInt_I16zh_static_info -Wl,-u,_base_GHCziInt_I32zh_static_info -Wl,-u,_base_GHCziInt_I64zh_static_info -Wl,-u,_base_GHCziWord_W8zh_static_info -Wl,-u,_base_GHCziWord_W16zh_static_info -Wl,-u,_base_GHCziWord_W32zh_static_info -Wl,-u,_base_GHCziWord_W64zh_static_info -Wl,-u,_base_GHCziStable_StablePtr_static_info -Wl,-u,_ghczmprim_GHCziTypes_Izh_con_info -Wl,-u,_ghczmprim_GHCziTypes_Czh_con_info -Wl,-u,_ghczmprim_GHCziTypes_Fzh_con_info -Wl,-u,_ghczmprim_GHCziTypes_Dzh_con_info -Wl,-u,_base_GHCziPtr_Ptr_con_info -Wl,-u,_base_GHCziPtr_FunPtr_con_info -Wl,-u,_base_GHCziStable_StablePtr_con_info -Wl,-u,_ghczmprim_GHCziTypes_False_closure -Wl,-u,_ghczmprim_GHCziTypes_True_closure -Wl,-u,_base_GHCziPack_unpackCString_closure -Wl,-u,_base_GHCziIOziException_stackOverflow_closure -Wl,-u,_base_GHCziIOziException_heapOverflow_closure -Wl,-u,_base_ControlziExceptionziBase_nonTermination_closure -Wl,-u,_base_GHCziIOziException_blockedIndefinitelyOnMVar_closure -Wl,-u,_base_GHCziIOziException_blockedIndefinitelyOnSTM_closure -Wl,-u,_base_ControlziExceptionziBase_nestedAtomically_closure -Wl,-u,_base_GHCziWeak_runFinalizzerBatch_closure -Wl,-u,_base_GHCziTopHandler_flushStdHandles_closure -Wl,-u,_base_GHCziTopHandler_runIO_closure -Wl,-u,_base_GHCziTopHandler_runNonIO_closure -Wl,-u,_base_GHCziConcziIO_ensureIOManagerIsRunning_closure -Wl,-u,_base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure -Wl,-u,_base_GHCziConcziSync_runSparks_closure -Wl,-u,_base_GHCziConcziSignal_runHandlers_closure -Wl,-search_paths_first -lHSbase-4.7.0.1 -lHSinteger-gmp-0.5.1.0 -lHSghc-prim-0.3.1.0 -lHSrts -lCffi -liconv -lgmp -lm -ldl
link: done
it seems during unwind, catch code was ignored by some reason. this behavior seems can only repeated under OSX with clang. I have tried gcc under Linux which works perfectly fine.