No effect at applying of ON_CALL on a gmock object - dependency-injection

For some fun purposes, I'm writing a Tetris game. I created two classes: "GameBackground" and "Tetromino". Since the "Tetromino" class has a dependency on the "GameBackground" class, I injected this dependency in the constructor using an interface such that I can test the "Tetromino" class independent of the GameBackground class by mocking it. When I use gmock to create the "GameBackgroundMock", the test fails. When I use my own mock like
class MyOwnMock : public IGameBackground {
public:
MyOwnMock() : IGameBackground() {
};
bool RequestSpaceOnGrid(TetrominoPositionType requested_coordinates) override{
return true;
}
};
then the test passes. Apparently, I don't use the gmock in the right way. I would appreciate any suggestion leading to a green test by using gmock.
For implementation details see the following code basis.
My initial folder structure looks as follows:
[Project structure][1]
[1]: https://i.stack.imgur.com/7h8zT.png
The corresponding files at the project's top-level:
CMakeLists.txt.in:
# see https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project
cmake_minimum_required(VERSION 2.8.2)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
CMakeLists.txt:
cmake_minimum_required(VERSION 3.11.3)
project(Test)
set(CMAKE_CXX_STANDARD 17)
# Download and unpack googletest at configure time
# (see https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project)
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
# Add googletest directly to our build. This defines the gtest and gtest_main targets.
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
add_subdirectory(src)
add_subdirectory(test)
The content of the src folder:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.11.3)
add_library(GameBackground_Lib STATIC GameBackground.cpp)
add_library(Tetromino_Lib STATIC Tetromino.cpp)
IGameBackground.h
#ifndef TEST_IGAMEBACKGROUND_H
#define TEST_IGAMEBACKGROUND_H
#include <utility>
#include <vector>
#include <iostream>
using TetrominoPositionType = std::vector<std::pair<int, int>>;
class IGameBackground {
public:
virtual ~IGameBackground() = default;
virtual bool RequestSpaceOnGrid(TetrominoPositionType) = 0;
};
#endif //TEST_IGAMEBACKGROUND_H
GameBackground.h
#ifndef TEST_GAMEBACKGROUND_H
#define TEST_GAMEBACKGROUND_H
#include "IGameBackground.h"
#include <tuple>
#include <utility>
#include <vector>
class GameBackground : public IGameBackground {
public:
GameBackground(int, int);
bool RequestSpaceOnGrid(TetrominoPositionType) override;
private:
int m_horizontal_grid_size;
int m_vertical_grid_size;
int m_nr_buttomlines_filled{};
std::vector<std::vector<bool>> m_occupancy_grid{};
};
#endif //TEST_GAMEBACKGROUND_H
GameBackground.cpp
#include "GameBackground.h"
GameBackground::GameBackground(int vertical_grid_size, int horizontal_grid_size)
: m_horizontal_grid_size{horizontal_grid_size},
m_vertical_grid_size{vertical_grid_size} {
m_occupancy_grid.resize(vertical_grid_size);
for (auto &row : m_occupancy_grid) {
row.resize(horizontal_grid_size);
}
}
bool GameBackground::RequestSpaceOnGrid(
TetrominoPositionType requested_coordinates) {
bool is_every_coordinate_within_bounds{true};
for (const auto &position : requested_coordinates) {
int pos_x{position.first}, pos_y{position.second};
if (pos_x < 0 || pos_x >= m_vertical_grid_size || pos_y < 0 ||
pos_y >= m_horizontal_grid_size) {
is_every_coordinate_within_bounds = false;
break;
}
}
bool is_request_successfull{false};
if (is_every_coordinate_within_bounds) {
try {
bool is_occupied{false};
for (const auto &position : requested_coordinates) {
int row{position.first}, column{position.second};
is_occupied |= m_occupancy_grid.at(row).at(column);
}
if (!is_occupied) {
for (const auto &position : requested_coordinates) {
int row{position.first}, column{position.second};
m_occupancy_grid.at(row).at(column) = true;
}
is_request_successfull = true;
}
} catch (std::out_of_range const &e) {
std::cerr << e.what() << '\n';
}
}
return is_request_successfull;
}
Tetromino.h
#ifndef TEST_TETROMINO_H
#define TEST_TETROMINO_H
#include <memory>
#include <utility>
#include <vector>
#include "IGameBackground.h"
enum class Direction { left, right, down };
using TetrominoPositionType = std::vector<std::pair<int, int>>;
class Tetromino {
public:
Tetromino(IGameBackground&, TetrominoPositionType);
TetrominoPositionType getPosition();
void setPosition(TetrominoPositionType);
void moveOneStep(Direction);
private:
TetrominoPositionType m_position;
IGameBackground& m_game_background;
};
#endif //TEST_TETROMINO_H
Tetromino.cpp
#include "Tetromino.h"
#include <utility>
Tetromino::Tetromino(IGameBackground &game_background,
TetrominoPositionType init_position)
: m_game_background{game_background},
m_position{std::move(init_position)} {};
TetrominoPositionType Tetromino::getPosition() { return m_position; }
void Tetromino::setPosition(TetrominoPositionType position) {
m_position = std::move(position);
}
void Tetromino::moveOneStep(Direction direction) {
TetrominoPositionType position = getPosition();
switch (direction) {
case Direction::down:
for (auto &pos : position) {
++pos.first;
}
if (m_game_background.RequestSpaceOnGrid(position)) {
setPosition(position);
}
break;
case Direction::left:
for (auto &pos : position) {
--pos.second;
}
if (m_game_background.RequestSpaceOnGrid(position)) {
setPosition(position);
}
break;
case Direction::right:
for (auto &pos : position) {
++pos.second;
}
if (m_game_background.RequestSpaceOnGrid(position)) {
setPosition(position);
}
break;
}
}
The content of the test folder
CMakeLists.txt
cmake_minimum_required(VERSION 3.11.3)
add_executable(TetrominoTest TetrominoTest.cpp)
target_link_libraries(TetrominoTest gmock_main gtest_main Tetromino_Lib)
TetrominoTest.cpp
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "../src/IGameBackground.h"
#include "../src/Tetromino.h"
//class MyOwnMock : public IGameBackground {
//public:
// MyOwnMock() : IGameBackground() {
// };
// bool RequestSpaceOnGrid(TetrominoPositionType requested_coordinates) override{
// return true;
// }
//};
class GameBackgroundMock : public IGameBackground {
public:
GameBackgroundMock() : IGameBackground() {
};
MOCK_METHOD(bool, RequestSpaceOnGrid,
(TetrominoPositionType requested_coordinates), (override));
};
class MoveTetromino : public ::testing::Test {
protected:
MoveTetromino() : unit_under_test(a_mock, init_position) {};
TetrominoPositionType init_position{{0, 0},
{0, 1},
{0, 2},
{0, 3}};
Tetromino unit_under_test;
GameBackgroundMock a_mock;
//MyOwnMock a_mock;
};
TEST_F(MoveTetromino, move_right) {
TetrominoPositionType current_position{init_position};
TetrominoPositionType expected_position{init_position};
for (auto &elem : expected_position) {
elem.second++;
}
ON_CALL(a_mock, RequestSpaceOnGrid(current_position)).WillByDefault(::testing::Return(true));
unit_under_test.moveOneStep(Direction::right);
TetrominoPositionType actual_position = unit_under_test.getPosition();
EXPECT_EQ(expected_position, actual_position);
}

You're using ON_CALL which sets a default value to be returned when a mocked method is being called. This is useful if you don't care how many times given method will be called in your tests and you just set a default value to be returned any number of times (however, expect calls are being matched first, it's a longer story).
It's better to use EXPECT_CALL in your case to explicitly state what actions you anticipate and validate them:
EXPECT_CALL(a_mock, RequestSpaceOnGrid(current_position)).WillOnce(::testing::Return(true));
If you will use this, gmock will let you know what's wrong with the test: the RequestSpaceOnGrid was called, however not with current_position but with expected_position. Not sure if this is expected, but this is what happens in the test.

The test passes by calling either
ON_CALL(a_mock, RequestSpaceOnGrid(expected_position)).WillByDefault(::testing::Return(true));
or as Quarra suggested
EXPECT_CALL(a_mock, RequestSpaceOnGrid(expected_position)).WillOnce(::testing::Return(true));
The crucial point was to give expected_position instead of current_position to the mocked RequestSpaceOnGrid method.
Another possibility would be to get the test green to call the mocked method with ::testing::_
ON_CALL(a_mock, RequestSpaceOnGrid(::testing::_)).WillByDefault(::testing::Return(true));
In this case, the mocked method would always return true independent of the given argument.

Related

Modifying clang AST using a plugin

I would like to modify the clang AST using a plugin.
I have following the suggestions of using Clang Rewriter object.
I use compiler.createDefaultOutputFile() to obtain the output stream to write the changed file into it.
the compilation works fine but the generated object file is broken.
I get the following linker error: "File format not recognized".
Build command line:
clang++-8 -Xclang -load -Xclang libcashpp.so -Xclang -add-plugin -Xclang cash-pp test.cpp
Build Output:
/tmp/test-eb7d3d.o: file not recognized: File format not recognized
Question:
Is it actually possible to update the AST using a clang plugin?
Can someone point me to an example program that does that?
Source Code:
#include <clang/Frontend/FrontendPluginRegistry.h>
#include <clang/AST/AST.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include "clang/Rewrite/Core/Rewriter.h"
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Sema/Sema.h>
#include <llvm/Support/raw_ostream.h>
#include <string>
#include <sstream>
namespace {
class VarDeclVisitor : public clang::RecursiveASTVisitor<VarDeclVisitor> {
public:
VarDeclVisitor(clang::Rewriter& rewriter)
: rewriter_(rewriter)
{}
bool VisitCallExpr(clang::CallExpr *ce) {
auto q = ce->getType();
auto t = q.getTypePtrOrNull();
if (t != NULL) {
auto callee = ce->getDirectCallee();
auto funcName = callee->getNameInfo().getAsString();
if (funcName == "getInstanceName") {
rewriter_.ReplaceText(ce->getBeginLoc(), callee->getIdentifier()->getName().size(), "getInstanceName2");
}
}
return true;
}
private:
clang::Rewriter& rewriter_;
};
class MyASTConsumer : public clang::ASTConsumer {
using Base = clang::ASTConsumer;
public:
MyASTConsumer(std::unique_ptr<llvm::raw_pwrite_stream>&& os)
: os_(std::move(os))
{}
void HandleTranslationUnit(clang::ASTContext &context) override {
clang::Rewriter rewriter(context.getSourceManager(), context.getLangOpts());
VarDeclVisitor visitor(rewriter);
auto &source_manager = context.getSourceManager();
const auto &mainFileID = source_manager.getMainFileID();
const auto &decls = context.getTranslationUnitDecl()->decls();
for (auto &decl : decls) {
const auto &fileID = source_manager.getFileID(decl->getLocation());
if (fileID != mainFileID)
continue;
visitor.TraverseDecl(decl);
}
auto rwbuf = rewriter.getRewriteBufferFor(mainFileID);
if (rwbuf) {
*os_ << std::string(rwbuf->begin(), rwbuf->end());
} else {
*os_ << source_manager.getBufferData(mainFileID);
}
}
private:
std::unique_ptr<llvm::raw_pwrite_stream> os_;
};
class MyPlugin : public clang::PluginASTAction {
public:
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &compiler,
llvm::StringRef inFile) override {
auto os = compiler.createDefaultOutputFile(false, inFile, "cpp");
if (!os)
return nullptr;
return std::make_unique<MyASTConsumer>(std::move(os));
}
bool ParseArgs(const clang::CompilerInstance &compiler,
const std::vector<std::string> &args) override {
return true;
}
void PrintHelp(llvm::raw_ostream &ros) {}
};
}
static clang::FrontendPluginRegistry::Add<MyPlugin>
X("cash-pp", "My plugin");

Derived clases and polymorphism

The given code for my program is:
class Hive
{
private:
Bee* queen;
Bee* worker;
Bee* drone;
public:
Hive();
void activity();
~Hive();
};
Coding instructions have been provided and I must add the code. My constructor as coded, has the following error:"Cannot allocate an object of abstract type"
Hive::Hive() //This is where my error is
{
//Create an instance of each derived class
queen = new Queen(1,1) ;
worker = new Worker(1,1) ;
drone = new Drone(1,1) ;
}
void Hive::activity()
{
//Polymorphically call each classes work function
queen->Work() ;
worker->Work() ;
drone->Work() ;
}
Hive::~Hive()
{
//Deal with dynamic memory
delete queen ;
delete worker ;
delete drone ;
}
My derived class definitions are:
class Drone : Bee /*Indicate that a Drone IS A Bee*/
{
private:
char fertile;
public:
Drone(int lifespan, char fertile);
virtual void work();
};
class Queen : Bee /*indicate that a Queen IS A Bee*/
{
private:
int numBabies;
public:
Queen(int lifespan, int numBabies);
virtual void work();
};
class Worker : Bee /*Indicate that a Worker IS A Bee*/
{
private:
int honeyMade;
public:
Worker(int lifespan, int honeyMade);
virtual void work();
};
Hopefully I didn't make additional mistakes.
The main:
include <iostream>
include "Hive.h"
using namespace std;
using namespace HiveSpace;
int main()
{
Hive h;
h.activity();
return 0;
}
Classes
#ifndef HIVE_H
#define HIVE_H
#include "Bee.h"
#include "Queen.h"
#include "Drone.h"
#include "Worker.h"
namespace HiveSpace
{
class Hive
{
private:
Bee* queen;
Bee* worker;
Bee* drone;
public:
Hive();
void activity();
~Hive();
};
}
#endif
///
#include "Hive.h"
#include <iostream>
using namespace std;
namespace HiveSpace
{
Hive::Hive()
{
//Create an instance of each derived class
queen = new Queen(1,1) ;
worker = new Worker(1,1) ;
drone = new Drone(1,1) ;
}
void Hive::activity()
{
//Polymorphically call each classes work function
queen->Work() ;
worker->Work() ;
drone->Work() ;
}
Hive::~Hive()
{
//Deal with dynamic memory
delete queen ;
delete worker ;
delete drone ;
}
}
///
#ifndef BEE_H
#define BEE_H
namespace HiveSpace
{
class Bee
{
private:
int lifespan;
public:
Bee(int lifespan);
//Add pure virtual function for work
virtual void Work() = 0 ;
//Create polymorphic destructor
virtual ~Bee() ; // ADDED
};
}
#endif
/////
#include "Bee.h"
#include <iostream>
using namespace std;
namespace HiveSpace
{
void Bee::Work()
{
//Nothing to do here
}
Bee::Bee(int lifespan)
{
cout << "Constructor for Bee base class " << endl;
this->lifespan = lifespan;
}
Bee::~Bee()
{
//Nothing special to do here
cout << "Destructor for Bee base class << endl;
}
}
///
#ifndef DRONE_H
#define DRONE_H
#include "Bee.h"
namespace HiveSpace
{
class Drone : Bee /*Indicate that a Drone IS A Bee*/
{
private:
char fertile;
public:
Drone(int lifespan, char fertile);
virtual void work();
};
}
#endif
///
#include "Drone.h"
#include <iostream>
using namespace std;
namespace HiveSpace
{
//Provide a constructor which also invokes the base class constructor
Drone::Drone(int lifespan, char fertile) : Bee(lifespan)
{
// this->fertile = fertile ;
}
void Drone::work()
{
cout << "Drone derived class working " << endl;
}
}
#ifndef QUEEN_H
#define QUEEN_H
#include "Bee.h"
namespace HiveSpace
{
class Queen : Bee /*indicate that a Queen IS A Bee*/
{
private:
int numBabies;
public:
Queen(int lifespan, int numBabies);
virtual void work();
};
}
#endif
///
#include "Queen.h"
#include <iostream>
using namespace std;
namespace HiveSpace
{
//Provide a constructor which also invokes the base class constructor
Queen::Queen(int lifespan, int numBabies) : Bee(lifespan)
{
this->numBabies = numBabies ;
}
void Queen::work()
{
cout << "Queen derived class working"<< endl;
numBabies++;
}
}
////
#ifndef WORKER_H
#define WORKER_H
#include "Bee.h"
namespace HiveSpace
{
class Worker : Bee /*Indicate that a Worker IS A Bee*/
{
private:
int honeyMade;
public:
Worker(int lifespan, int honeyMade);
virtual void work();
};
}
#endif
///
#include "Worker.h"
#include <iostream>
using namespace std;
namespace HiveSpace
{
//Provide a constructor which also invokes the base class constructor
Worker::Worker(int lifespan, int honeyMade) : Bee(lifespan)
{
this->honeyMade = honeyMade ;
}
void Worker::work()
{
cout << "Worker derived class working" << endl;
honeyMade++;
}
}

QTimer does not fire (trigger) timeout

I want to use Qtimer to pause executing of given block of codes, while in another thread it does something else. I connected the timeout of the thread with qeventloop quit, but the problem is, that the timeout is not called. When another emit occures, the timeout is magically triggered or if I add another connect the time out is triggered too. I think I miss something about the use of qtimer, qeventloop and qthread. Can anyone help? I extracted the basic code for testing and put it here:
main.cpp
#include "widget.h"
#include <QApplication>
#include "tim.h"
#include <QThread>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget *w=new Widget();
tim *t=new tim();
QThread *thread=new QThread();
t->moveToThread(thread);
thread->start();
QThread::connect(w,SIGNAL(signalDoIt()),t,SLOT(slotDoIt()),Qt::QueuedConnection);
QThread::connect(w,SIGNAL(signalQuitTimer()),t,SLOT(slotQuitTimer()),Qt::QueuedConnection);
QThread::connect(t,SIGNAL(signalSetText(QString)),w,SLOT(slotSetText(QString)),Qt::QueuedConnection);
w->show();
return a.exec();
}
tim.h
#ifndef TIM_H
#define TIM_H
#include <QObject>
#include<QTimer>
#include<QTime>
#include<QEventLoop>
#include<QThread>
#include<QDebug>
class tim : public QObject
{
Q_OBJECT
public:
tim();
~tim();
signals:
void signalSetText(QString);
public slots:
void slotDoIt();
void slotQuitTimer();
void slotShowTime();
private:
QTimer *trainingTimer;
QEventLoop loopTrainingWait;
QTime time;
};
#endif // TIM_H
tim.cpp
#include "tim.h"
tim::tim()
{
qDebug()<<"constructor";
trainingTimer=new QTimer(this);
trainingTimer->setTimerType(Qt::PreciseTimer);
trainingTimer->setSingleShot(true);
QThread::connect(trainingTimer,SIGNAL(timeout()),&loopTrainingWait,SLOT(quit()));
// QThread::connect(trainingTimer,SIGNAL(timeout()),this,SLOT(slotShowTime())); //to uncomment all works, but withou this, it does not
}
void tim::slotDoIt()
{
trainingTimer->start(5000);
time.start();
loopTrainingWait.exec();
QString text(QString::number(loopTrainingWait.isRunning())+" "+ QString::number(time.elapsed()));
qDebug()<<text;
emit signalSetText(text);
}
void tim::slotShowTime()
{
QString text(QString::number(loopTrainingWait.isRunning())+" slot "+ QString::number(time.elapsed()));
qDebug()<<text;
emit signalSetText(text);
}
void tim::slotQuitTimer()
{
if(loopTrainingWait.isRunning())
loopTrainingWait.quit();
if(trainingTimer->isActive())
trainingTimer->stop();
}
tim::~tim()
{
}
//gui for testing
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
public slots:
void slotSetText(QString text);
signals:
void signalDoIt();
void signalQuitTimer();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
void Widget::slotSetText(QString text)
{
ui->label->setText(text);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
this->close();
}
void Widget::on_pushButton_2_clicked()
{
emit signalDoIt();
}
void Widget::on_pushButton_3_clicked()
{
emit signalQuitTimer();
}
I met the same issue,and eventually I wrote SLOT between public and :,then it works
the problem was in static qeventloop, making this dynamic
loopTrainingWait=new QEventLoop(this);
with parent this, resolved the issue

boost::asio and boost::bind errors

This questions is a bit annoying, I can't get the following code to compile. You will have to compile the code below.
I am having some trouble with boost asio, I am trying to abstract the logic of accepting connections into a uniform abstraction so that I can initiate connection for windows named-pipes and Unix domain sockets uniformly with regular TCP/IP.
There are 3 classes shown in the code below, the first 2 are the implementations of acceping TCP connections, and the third class below is a generic class that is implemented in terms of the first 2. I am having troubles with boost::bind calls. The trouble probably lies with my understanding of the semantics.
If I make TcpDestinationAcceptor::handle_accept a regular member function (--i.e., not a template member function) which results in me not passing the AcceptHandler parameter. The code compiles fine. Note: I do not remove the template function status from TcpDestinationAcceptor::StartAccepting.
Note: I have already started on a different design, still I would like to pursue this design if possible.
Self contained code:
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/asio/placeholders.hpp>
class TcpDestinationConnection
{
public:
typedef boost::asio::ip::tcp::socket t_io_object;
TcpDestinationConnection(boost::asio::io_service & io_s)
: m_io_object(io_s) {} ;
t_io_object & io_object() { return m_io_object; }
private:
t_io_object m_io_object;
};
class TcpDestinationAcceptor
{
public:
typedef boost::asio::ip::tcp::acceptor t_acceptor;
typedef boost::shared_ptr<TcpDestinationConnection> t_connection_ptr;
TcpDestinationAcceptor( boost::asio::io_service & io_s)
: m_io_service(io_s),
m_acceptor(io_s)
{
m_acceptor.open(boost::asio::ip::tcp::v4());
}
TcpDestinationAcceptor( boost::asio::io_service & io_s ,
const boost::asio::ip::tcp::endpoint & endpoint)
: m_io_service(io_s),
m_acceptor(io_s, endpoint)
{
m_acceptor.open(boost::asio::ip::tcp::v4());
}
t_acceptor & acceptor() { return m_acceptor; }
template<typename AcceptHandler>
void StartAccepting(AcceptHandler h)
{
t_connection_ptr new_session(new TcpDestinationConnection(m_io_service));
m_acceptor.async_accept( new_session->io_object(),
boost::bind( &TcpDestinationAcceptor::handle_accept<AcceptHandler>, this,
boost::asio::placeholders::error, new_session, h));
}
template<typename AcceptHandler>
void handle_accept(const boost::system::error_code & err, t_connection_ptr cur, AcceptHandler h) {
}
private:
boost::asio::io_service & m_io_service;
boost::asio::ip::tcp::acceptor m_acceptor;
};
template<typename t_acceptor>
class ConnectionOracle
{
public:
ConnectionOracle()
: m_io_service(),
m_acceptor(m_io_service) {}
typename t_acceptor::t_acceptor & native_acceptor() { return m_acceptor.acceptor(); }
boost::asio::io_service & io_service() { return m_io_service; }
void StartConnection( typename t_acceptor::t_connection_ptr connection,
boost::system::error_code & error)
{
}
void Begin()
{
m_acceptor.StartAccepting( boost::bind( &ConnectionOracle::StartConnection,this,
_1,
boost::asio::placeholders::error));
m_io_service.run();
}
private:
boost::asio::io_service m_io_service;
t_acceptor m_acceptor;
};
int main()
{
ConnectionOracle<TcpDestinationAcceptor> ConOracle;
ConOracle.native_acceptor().
bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),50000));
ConOracle.Begin();
return 0;
}

In Gobject, how to override parent class's method belong to an interface?

GObject class A implements interface IA, B is a derived class of A. How can B override A's method that is part of the interface IA?
Or, is this possible in GObject?
I know how to override parent class methods, but when inheritance meets interface, things seems to be more complicated.
Thanks a lot!
Yes, it is possible: just reimplement the interface as it was the first time, either using G_IMPLEMENT_INTERFACE() or manual initializing it in your get_type() function.
The real pain is if you need to chain up the old method. In this case, you should play with
g_type_interface_peek_parent to get the previous interface class.
Here is a test case:
/* gcc -otest `pkg-config --cflags --libs gobject-2.0` test.c */
#include <glib-object.h>
/* Interface */
#define TYPE_IFACE (iface_get_type())
typedef void Iface;
typedef struct {
GTypeInterface parent_class;
void (*action) (Iface *instance);
} IfaceClass;
GType
iface_get_type(void)
{
static GType type = 0;
if (G_UNLIKELY(type == 0)) {
const GTypeInfo info = {
sizeof(IfaceClass), 0,
};
type = g_type_register_static(G_TYPE_INTERFACE, "Iface", &info, 0);
}
return type;
}
void
iface_action(Iface *instance)
{
G_TYPE_INSTANCE_GET_INTERFACE(instance, TYPE_IFACE, IfaceClass)->
action(instance);
}
/* Base object */
#define TYPE_BASE (base_get_type())
typedef GObject Base;
typedef GObjectClass BaseClass;
static void
base_action(Iface *instance)
{
g_print("Running base action on a `%s' instance...\n",
g_type_name(G_TYPE_FROM_INSTANCE(instance)));
}
static void
base_iface_init(IfaceClass *iface)
{
iface->action = base_action;
}
G_DEFINE_TYPE_WITH_CODE(Base, base, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(TYPE_IFACE, base_iface_init));
static void
base_class_init(BaseClass *klass)
{
}
static void
base_init(Base *instance)
{
}
/* Derived object */
#define TYPE_DERIVED (derived_get_type())
typedef Base Derived;
typedef BaseClass DerivedClass;
static void
derived_action(Iface *instance)
{
IfaceClass *iface_class, *old_iface_class;
iface_class = G_TYPE_INSTANCE_GET_INTERFACE(instance, TYPE_IFACE, IfaceClass);
old_iface_class = g_type_interface_peek_parent(iface_class);
g_print("Running derived action on a `%s' instance...\n",
g_type_name(G_TYPE_FROM_INSTANCE(instance)));
/* Chain up the old method */
old_iface_class->action(instance);
}
static void
derived_iface_init(IfaceClass *iface)
{
iface->action = derived_action;
}
G_DEFINE_TYPE_WITH_CODE(Derived, derived, TYPE_BASE,
G_IMPLEMENT_INTERFACE(TYPE_IFACE, derived_iface_init));
static void
derived_class_init(DerivedClass *klass)
{
}
static void
derived_init(Derived *instance)
{
}
int
main()
{
GObject *object;
g_type_init();
object = g_object_new(TYPE_BASE, NULL);
iface_action((Iface *) object);
g_object_unref(object);
object = g_object_new(TYPE_DERIVED, NULL);
iface_action((Iface *) object);
g_object_unref(object);
return 0;
}
I think a better solution would be to make A's method virtual, rather than have B re-implement the interface A is attached to (this may require more work than just redefining one function), which you can do like this (example should be complete other than the fooable interface definition):
#include <glib-object.h>
#include "fooable.h"
typedef struct {GObject parent;} A;
typedef struct {
GObjectClass parent;
gint (*foo) (Fooable *self, gdouble quux);
} AClass;
#define TYPE_A (a_get_type())
#define A_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), TYPE_A, AClass))
#define A_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_A, AClass))
gint a_foo_real (Fooable *self, gdouble quux) {
g_print("a_foo_real(%g)\n", quux);
return 5;
}
gint a_foo (Fooable *self, gdouble quux) {
return A_GET_CLASS(self)->foo(self, quux);
}
void implement_fooable (FooableIface *iface) {iface->foo = a_foo;}
void a_class_init (AClass *cls) {cls->foo = a_foo_real;}
void a_init (A *self) {}
G_DEFINE_TYPE_WITH_CODE(A, a, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(TYPE_FOOABLE, implement_fooable));
/* derive class B from A */
typedef struct {A parent;} B;
typedef struct {AClass parent;} BClass;
#define TYPE_B (b_get_type())
gint b_foo_real (Fooable *self, gdouble quux) {
g_print("b_foo_real(%g)\n", quux);
return 55;
}
void b_class_init (BClass *cls) {A_CLASS(cls)->foo = b_foo_real;}
void b_init (B *self) {}
G_DEFINE_TYPE(B, b, TYPE_A);
int main () {
g_type_init();
A *a = g_object_new(TYPE_A, NULL);
B *b = g_object_new(TYPE_B, NULL);
fooable_foo(FOOABLE(a), 87.0); // a_foo_real(87.0) and returns 5
fooable_foo(FOOABLE(b), 32.0); // b_foo_real(32.0) and returns 55
return 0;
}
That's as brief of an example as I can make it. When you call fooable_foo() the function will look at its vtable for the function defined when you implemented the interface which is a_foo() which looks at A class's vtable to determine which function to actually call. The B class definition overrides A class's a_foo_real() with its own. If you need B class's b_foo_real to chain up, that's an easy enough (use A_CLASS(b_parent_class)->foo() which is defined for you in the G_DEFINE_TYPE macro)

Resources