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