不知道为什么我会收到 std::__1 未定义符号错误

No Idea Why I'm getting std::__1 Undefined Symbols Error

我对 C++/CMake 很陌生,正在尝试创建一个可以编译 GRPC 项目的 CMake 文件。我可以使用此 CMake 生成客户端(只需要客户端)文件,但我正在尝试创建另一个文件(base.cpp),它将从一个导入 class(ManagerClient)这些文件。我决定为我想要的这个文件创建一个头文件 manager_client.h.

我不太确定在 CMake 文件中的哪个位置编译 base.cpp 所以将其添加为目标目录之一并假设我制作的头文件会自动链接。在 运行ning make 之后,我收到此错误:

Undefined symbols for architecture x86_64:
  "ManagerClient::ManagerClient(std::__1::shared_ptr<grpc::Channel>)", referenced from:
      ReadUntilClient::ReadUntilClient() in base.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [base] Error 1
make[1]: *** [CMakeFiles/base.dir/all] Error 2
make: *** [all] Error 2

我找到了这个 ,但没有真正解释我如何解决我的问题。奇怪的是在制作 base.cpp 之前,我能够在 manager_client.cpp 的主要功能中 运行 base.cpp 逻辑没有任何问题,所以我怀疑我' m 通过 CMake 编译错误或者我的头文件是错误的。有什么想法是从哪里来的吗?

代码如下:

manager_client.cpp

#include <grpcpp/grpcpp.h>
#include <grpc/grpc.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include <string>
#include <memory>

#include "minknow_api/manager.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using grpc::ClientReader;
using minknow_api::manager::ManagerService;
using minknow_api::manager::FlowCellPositionsRequest;
using minknow_api::manager::FlowCellPositionsResponse;


class ManagerClient {
  public:
    ManagerClient(std::shared_ptr<Channel> channel) : stub_(ManagerService::NewStub(channel)) {}

  std::unique_ptr<ClientReader<FlowCellPositionsResponse> > flow_cell_positions(FlowCellPositionsRequest request) {
      ClientContext context;
      return stub_->flow_cell_positions(&context, request);
  }
  private:
    std::unique_ptr<ManagerService::Stub> stub_;
};

int main(int argc, char* argv[]) {
  return 0;
}

manager_client.h

#pragma once

#include <grpcpp/grpcpp.h>
#include <grpc/grpc.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include <string>
#include <memory>

#include "minknow_api/manager.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using grpc::ClientReader;
using minknow_api::manager::ManagerService;
using minknow_api::manager::FlowCellPositionsRequest;
using minknow_api::manager::FlowCellPositionsResponse;

class ManagerClient {
  public:
    ManagerClient(std::shared_ptr<Channel> channel);
    std::unique_ptr<ClientReader<FlowCellPositionsResponse> > flow_cell_positions(FlowCellPositionsRequest request);
  private:
    std::unique_ptr<ManagerService::Stub> stub_;
};

base.cpp

#include <grpcpp/grpcpp.h>
#include <grpc/grpc.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include <string>
#include <thread>
#include <memory>
#include <iostream>

#include "manager_client.h"
#include "minknow_api/manager.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using grpc::ClientReader;
using minknow_api::manager::FlowCellPositionsRequest;
using minknow_api::manager::FlowCellPositionsResponse;

class ReadUntilClient {
  public:
    ManagerClient manager;
    ReadUntilClient() : manager(grpc::CreateChannel("127.0.0.1:9501",
                          grpc::InsecureChannelCredentials())) {}
};

int main(int argc, char* argv[]) {
  ReadUntilClient client;
  return 0;
}

CMakeLists.txt

// Above this I compile the proto files & I only included the code for relevant files
add_library(rg_grpc_proto
  ${man_grpc_srcs}
  ${man_grpc_hdrs}
  ${man_proto_srcs}
  ${man_proto_hdrs})
target_link_libraries(rg_grpc_proto
  ${_REFLECTION}
  ${_GRPC_GRPCPP}
  ${_PROTOBUF_LIBPROTOBUF})

foreach(_target
  manager_client
  base)
  add_executable(${_target}
    "${_target}.cpp")
  target_link_libraries(${_target}
    rg_grpc_proto
    ${_REFLECTION}
    ${_GRPC_GRPCPP}
    ${_PROTOBUF_LIBPROTOBUF})
endforeach()

您似乎误解了具有多个源文件的项目是如何工作的。

在一个有多个源文件的项目中,源文件通常被一个一个地构建成目标文件,然后目标文件被链接(与任何库一起)成为可执行程序文件。

使用 CMake,这是通过添加 one 可执行目标(带有 add_executable)来完成的,该目标列出了要用于该程序的所有源文件。喜欢:

add_executable(program base.cpp manager_client.cpp)

这将导致构建从两个源文件 base.cppmanager_client.cpp 创建可执行程序文件 program(使用上述中间目标文件和链接器步骤)。


如前所述,此更改意味着您不应在源文件中重新定义 ManagerClient class,而是 #include 头文件。

您也不应该在每个源文件中都有一个 main 函数,应该只在一个源文件中定义它。