Home > Net >  Google test using Bazel error - `symbol not found in flat namespace`
Google test using Bazel error - `symbol not found in flat namespace`

Time:12-16

I have recently started learning C (from a Python background) and the Bazel build system. I have a very simple toy set-up to get used to using GoogleTest on Bazel. Here are my files -

My file and directory structure are as follows -

.
  WORKSPACE
  main/
    shape.cc
    shape.h
    BUILD
  test/
    shape_test.cc
    BUILD

The contents of the files are as follows -

# 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


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

#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

#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);
}

and finally the test folder

# 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

#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);
}

I now run the following command - bazel test --test_output=all //test:shape_test, but I get this undecipherable error -

INFO: From Testing //test:shape_test:
==================== Test output for //test:shape_test:
dyld[37648]: symbol not found in flat namespace (__ZTV13AbstractShape)
================================================================================

What does it mean "symbol not found"? Has anyone faced a similar problem with Bazel/cpp on a macos before?

CodePudding user response:

The error means not all virtual methods are defined. The exact one virtual AbstractShape::get_description is not defined. I guess from the class name, the member function must be pure virtual

virtual std::string get_description() = 0;
//                                    ^

The class must have the virtual destructor

virtual ~AbstractShape() = default;

CodePudding user response:

Thanks! Turns out I was missing a virtual destructor and I didn't make the member function pure virtual.

This is the final cpp file that works -

#include "shape.h"
#include <vector>
#include <string>
#include <iostream>
#include <cmath>
#include "fmt/core.h"

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: "   fmt::format("{:.2f}", this->perimeter)   "\n"  
                              "Area: "   fmt::format("{:.2f}", this->area)   "\n";

    return description;
}

ColourAndSurfaceRectangle::ColourAndSurfaceRectangle(float h, float w, std::string colour, std::string surface)
{
    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"  
                              "Perimeter:"   fmt::format("{:.2f}", this->perimeter)   "\n"  
                              "Area:"   fmt::format("{:.2f}", 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 new ColourAndSurfaceRectangle(h, w, colour, surface);
    }
    return new Rectangle(h, w);
}

and the header file -

#ifndef MAIN_SHAPE_H_
#define 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() = 0;
    virtual ~AbstractShape() = default;
};

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();
};

#endif
  • Related