使用Bazel运行Google Test遇`symbol not found in flat namespace`错误
__ZTV13AbstractShape符号未找到问题排查 项目背景
我之前是Python开发者,最近开始学习C++和Bazel构建系统,搭建了一个用于熟悉Google Test的简单测试项目,执行测试时遇到符号未找到错误,具体信息及项目代码如下:
项目文件结构
. WORKSPACE main/ shape.cc shape.h BUILD test/ shape_test.cc BUILD
各文件内容
WORKSPACE文件
# WORKSPACE file - from http://google.github.io/googletest/quickstart-bazel.html load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "com_google_googletest", urls = ["https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip"], strip_prefix = "googletest-609281088cfefc76f9d0ce82e1ff6c30cc3591e5", )
main/BUILD文件
# main/BUILD load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") package(default_visibility = ["//visibility:public"]) cc_library( name = "shape", srcs = ["shape.cc"], hdrs = ["shape.h"], )
main/shape.h文件
// main/shape.h #include <string> #include <vector> class AbstractShape { protected: float area; float perimeter; std::string shape_name; public: float get_area(); void describe(); virtual std::string get_description(); }; AbstractShape factory_method(float h, float w, std::string colour = "", std::string surface = ""); float calculate_area(std::vector<AbstractShape> shape_arr); class Rectangle : public AbstractShape { public: Rectangle(float h, float w); std::string get_description(); }; class ColourAndSurfaceRectangle : public AbstractShape { private: std::string colour; std::string surface; public: ColourAndSurfaceRectangle(float h, float w, std::string colour = "", std::string shape = ""); std::string get_description(); };
main/shape.cc文件
// main/shape.cc #include "shape.h" #include <vector> #include <string> #include <iostream> float calculate_area(std::vector<AbstractShape> shape_arr) { float sum = 0; for (AbstractShape shape : shape_arr) { sum += shape.get_area(); } return sum; } float AbstractShape::get_area() { return area; } void AbstractShape::describe() { std::string description = this->get_description(); std::cout << description << std::endl; } Rectangle::Rectangle(float h, float w) { this->shape_name = "Rectangle"; this->area = h * w; this->perimeter = 2 * (h + w); } std::string Rectangle::get_description() { std::string description = "Shape:" + this->shape_name + "\n" + "Perimeter: " + std::to_string(this->perimeter) + "\n" + "Area: " + std::to_string(this->area) + "\n"; return description; } ColourAndSurfaceRectangle::ColourAndSurfaceRectangle(float h, float w, std::string colour, std::string shape) { this->shape_name = "Rectangle"; this->area = h * w; this->perimeter = 2 * (h + w); this->colour = colour; this->surface = surface; } std::string ColourAndSurfaceRectangle::get_description() { std::string description = "Shape:" + this->shape_name + "\n" + "Perimeter: " + std::to_string(this->perimeter) + "\n" + "Area: " + std::to_string(this->area) + "\n"; if (this->colour != "") { description += "Colour: " + this->colour + "\n"; } if (this->surface != "") { description += "Surface: " + this->surface + "\n"; } return description; } AbstractShape factory_method(float h, float w, std::string colour, std::string surface) { if (colour != "" || surface != "") { return ColourAndSurfaceRectangle(h, w, colour, surface); } return Rectangle(h, w); }
test/BUILD文件
# test/BUILD cc_test( name = "shape_test", size = "small", srcs = ["shape_test.cc"], deps = [ "//main:shape", "@com_google_googletest//:gtest_main", ], )
test/shape_test.cc文件
// test/shape_test.cc #include <gtest/gtest.h> #include "main/shape.h" #include <string> TEST(ColourAndSurfaceRectangleTest, ShapeTest) { std::string expected_str = "Shape: Rectangle\n" "Perimeter: 10\n" "Area: 6\n" "Colour: Blue\n" "Surface: Stained\n"; AbstractShape shape = factory_method(3, 2, "Blue", "Stained"); EXPECT_EQ(shape.get_description(), expected_str); }
错误信息
执行命令 bazel test --test_output=all //test:shape_test 后出现以下错误:
INFO: From Testing //test:shape_test: ==================== Test output for //test:shape_test: dyld[37648]: symbol not found in flat namespace (__ZTV13AbstractShape) ================================================================================
问题
- 这个
symbol not found错误是什么含义? - macOS下使用Bazel/C++的用户有没有遇到过同类问题?
解答
错误含义
__ZTV13AbstractShape是C++编译器生成的虚函数表(vtable)符号,对应AbstractShape类的虚函数表。出现“symbol not found”说明程序运行时找不到这个虚函数表,核心原因是对象切片问题:factory_method返回AbstractShape对象时,实际返回的是子类对象被“切片”后的基类对象——子类特有的数据和虚函数表信息被丢弃,调用虚函数get_description()时,无法找到正确的虚函数表,进而触发符号缺失错误。
macOS下同类问题
不少macOS用户用Bazel构建C项目时遇到过类似的虚函数表符号缺失问题,本质大多是C面向对象特性使用不当(比如对象切片),并非Bazel或macOS的特有bug。
修复方案
将
AbstractShape改为纯抽象类
把get_description()声明为纯虚函数,确保基类无法被实例化,从根源避免对象切片:virtual std::string get_description() = 0; // 纯虚函数修改返回值类型为智能指针
factory_method返回std::unique_ptr<AbstractShape>,这样不会发生对象切片,能完整保留子类的虚函数表信息:// shape.h中修改声明 std::unique_ptr<AbstractShape> factory_method(float h, float w, std::string colour = "", std::string surface = ""); // shape.cc中修改实现 std::unique_ptr<AbstractShape> factory_method(float h, float w, std::string colour, std::string surface) { if (colour != "" || surface != "") { return std::make_unique<ColourAndSurfaceRectangle>(h, w, colour, surface); } return std::make_unique<Rectangle>(h, w); }调整测试代码
使用指针调用虚函数,同时注意std::to_string会输出浮点数的完整格式(比如10会变成10.000000),需调整预期字符串匹配实际输出:TEST(ColourAndSurfaceRectangleTest, ShapeTest) { std::string expected_str = "Shape:Rectangle\n" "Perimeter: 10.000000\n" "Area: 6.000000\n" "Colour: Blue\n" "Surface: Stained\n"; auto shape = factory_method(3, 2, "Blue", "Stained"); EXPECT_EQ(shape->get_description(), expected_str); }
内容的提问来源于stack exchange,提问作者mndl

