I have a problem to properly setup a test for a turtle-graphic interface.
As sample I have a simplified interface for just drawing a line.
Now I want to write a test for drawing some grid draw_grid and ensure that each line of the grid is drawn, but the actual order of drawn lines does not matter. I only need to ensure, that the method calls to move_to and line_to are properly paired. I tried this with InSequence but this leads to several issues, because a couple of line_to and move_to calls are used for two lines.
For example move_to(0,0) is used for the top edge of the grid and for the left border of the first cell. So the test will generate two EXPACT_CALL(sot, move_to(0,0)) in two different sequences, but each of them implicitly with Times(1). So I guess this is the main problem. This will happen for each left and top border of a row and similarly for line_to for right and bottom border of a row.
I also tried to use After for an EXPACT_CALL, but this just leads to different test errors.
Is there any nice way to specify the requested behavior? Thanks for your help!
using testing::InSequence;
struct ITurtle
{
virtual void move_to(int x, int y) = 0;
virtual void line_to(int x, int y) = 0;
void line(int x0, int y0, int x1, int y1) { move_to(x0, y0); line_to(x1, y1); }
};
class TurtleMock
: public ITurtle
{
public:
MOCK_METHOD(void, move_to, (int x, int y), (override));
MOCK_METHOD(void, line_to, (int x, int y), (override));
};
void draw_grid(ITurtle &t)
{
for (int r = 0; r < 100; r += 10)
{
// top
t.line(0,r,100,r);
for (int c = 0; c < 100; c += 10)
{ // left
t.line(c,r,c,r+10);
}
// right
t.line(100,r,100,r+10);
}
// bottom
t.line(0,100,100,100);
}
TEST(TurtleTest, lines)
{
TurtleMock sot;
for (int r = 0; r < 100; r += 10)
{
{
InSequence s;
EXPECT_CALL(sot, move_to(0, r));
EXPECT_CALL(sot, line_to(100, r));
}
for (int c = 0; c < 100; c += 10)
{
InSequence s;
EXPECT_CALL(sot, move_to(c,r));
EXPECT_CALL(sot, line_to(c,r+10));
}
{
InSequence s;
EXPECT_CALL(sot, move_to(100, r));
EXPECT_CALL(sot, line_to(100, r + 10));
}
}
{
InSequence s;
EXPECT_CALL(sot, move_to(0, 100));
EXPECT_CALL(sot, line_to(100, 100));
}
draw_grid(sot);
}
EDIT:
Extension to proposed solution of Sedenion to support polygon drawing with one initial move_to followed by a sequence of line_to statements.
void move_to(int x, int y) final
{
m_move_to_data = {x, y};
}
void line_to(int x, int y) final
{
ASSERT_TRUE(m_move_to_data.has_value());
auto const [x0, y0] = *m_move_to_data;
line_mock(x0, y0, x, y);
m_move_to_data = {x, y};
}
Consider the following simplified example of drawing just 2 lines (live example):
void draw_two_lines(ITurtle &t)
{
t.line(0, 0, 10, 0);
t.line(0, 0, 0, 10);
}
TEST(TurtleTest, two_lines)
{
TurtleMock sot;
{
InSequence s;
EXPECT_CALL(sot, move_to(0, 0)); // (2)
EXPECT_CALL(sot, line_to(10, 0));
}
{
InSequence s;
EXPECT_CALL(sot, move_to(0, 0)); // (1)
EXPECT_CALL(sot, line_to(0, 10));
}
draw_two_lines(sot);
}
Forgetting about InSequence for a moment, expectations are matched in reverse order (from bottom to top) by default by Google Mock.
Quote from the manual:
By default, when a mock method is invoked, gMock will search the expectations in the reverse order they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as “newer rules override older ones.”).
Now, here we do have InSequence, i.e. two groups. However, the two groups themselves are not "in sequence". That means gMock will match the first call to move_to(0, 0) with the second group (marked by (1) in the code above). Thus, afterwards, line_to(0, 10) is expected but line_to(10, 0) gets called, resulting in a test failure. If you exchange the order of the two InSequence-groups, the test will pass. However, this is not really worth anything since your goal is to have the order independent.
What you want is basically to specify something like one "atomic" match of all 4 parameters.
I am not aware of any way to directly express this with the InSequence or After machinery of GoogleMock. Thus, I propose to take another approach and store the call of move_to in a temporary variable, and in the call of line_to take the remembered two values and the two given values to call a dedicated mock function (live example):
struct ITurtle
{
virtual void move_to(int x, int y) = 0;
virtual void line_to(int x, int y) = 0;
void line(int x0, int y0, int x1, int y1) { move_to(x0, y0); line_to(x1, y1); }
};
class TurtleMock : public ITurtle
{
public:
std::optional<std::pair<int, int>> move_to_data;
virtual void move_to(int x, int y) final {
ASSERT_FALSE(move_to_data.has_value());
move_to_data = {x, y};
}
virtual void line_to(int x1, int y1) final {
ASSERT_TRUE(move_to_data.has_value());
auto const [x0, y0] = *move_to_data;
line_mock(x0, y0, x1, y1);
move_to_data.reset();
}
MOCK_METHOD(void, line_mock, (int x0, int y0, int x1, int y1));
};
void draw_two_lines(ITurtle &t)
{
t.line(0, 0, 10, 0);
t.line(0, 0, 0, 10);
}
TEST(TurtleTest, two_lines)
{
TurtleMock sot;
EXPECT_CALL(sot, line_mock(0, 0, 10, 0));
EXPECT_CALL(sot, line_mock(0, 0, 0, 10));
draw_two_lines(sot);
}
This allows to specify all 4 parameters in one "atomic" match, making the whole stuff with InSequence unnecessary. The above example passes regardless of the order of the line() calls in draw_two_lines().
Your test for draw_grid() would then become (live example):
TEST(TurtleTest, grid)
{
TurtleMock sot;
for (int r = 0; r < 100; r += 10)
{
EXPECT_CALL(sot, line_mock(0, r, 100, r));
for (int c = 0; c < 100; c += 10) {
EXPECT_CALL(sot, line_mock(c, r, c, r+10));
}
EXPECT_CALL(sot, line_mock(100, r, 100, r + 10));
}
EXPECT_CALL(sot, line_mock(0, 100, 100, 100));
draw_grid(sot);
}
Note: This solution assumes that you cannot or do not want to make ITurtle::line() virtual. If it were, you could of course ditch the helper move_to_data and line_mock() and instead mock line() directly.
Related
I'm trying to code metaballs in C++/SFML, my program works just fine in a single thread. I tried to write an MRE to find the problem and here's what I got:
main.cpp
// main
#include <iostream>
#include "threader.h"
float func(float x, float y, float a, float b, float r) {
return 1.f / sqrt((x - a)*(x - a) + (y - b)*(x - b));
}
int main () {
sf::RenderWindow window(sf::VideoMode(2800, 1800), "");
window.setFramerateLimit(20);
sf::Event event{};
threader tf(window);
while (window.isOpen()) {
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed: {
window.close();
}
}
}
window.clear();
tf.parallel(func);
window.display();
}
}
threader.h
//threader_H
#pragma once
#include <array>
#include <thread>
#include <cmath>
#include <functional>
#include <SFML/Graphics.hpp>
class threader {
public:
int threadCount = 4;
std::array<std::thread, 4> threads;
sf::RenderWindow& window;
public:
explicit threader(sf::RenderWindow& w) : window(w) {};
void strip(int start, int end, const std::function<float(float, float, float, float, float)>& func);
void parallel(const std::function<float(float, float, float, float, float)>& func);
};
threader.cpp
// threader_CPP
#include "threader.h"
#include <iostream>
void threader::strip (int start, int end, const std::function<float (float, float, float, float, float)> &func) {
for (int X = start; X < end; X += 10) {
for (int Y = 0; Y < window.getSize().y; Y += 10) {
auto x = static_cast<float>(X);
auto y = static_cast<float>(Y);
x = x / 2800.f * 4 + 0 - 4 / 2.f;
y = -((1800.f - y) / 1800 * 4 + 0 - 4 / 2.f);
if (func(x, y, 1, 2, 3) > 1) {
sf::CircleShape circle(20);
circle.setPointCount(3);
circle.setFillColor(sf::Color::Cyan);
circle.setPosition(X, Y);
window.draw(circle);
}
}
}
}
void threader::parallel (const std::function<float (float, float, float, float, float)> &func) {
int start = 0;
int end = window.getSize().x / threadCount;
for (auto& t : threads) {
t = std::thread(&threader::strip, this, start, end, func);
start = end;
end += window.getSize().x / threadCount;
}
for (auto& t : threads) {
t.join();
}
}
Now for the explanation. threader is a class which has two methods: strip that calculates a function for a given strip of the window and parallel that creates threads to separately calculate my function for every strip. This code doesn't work:
But here's the catch: if I adjust the function void func(...) in main to return 1.f / sqrt((x - a) + (y - b)), everything works just fine. What is happening? How a simple calculation can cause a segfault? help please...
EDIT 1: Written in CLion, C++ 20.
EDIT 2: If anything here makes sense, please explain it to me.
Rather than try to draw to the window on multiple threads, I would instead use std algorithms to filter the points to draw circles, and draw them all on the main thread.
std::vector<std::pair<int, int>> getPoints(const sf::RenderWindow& window) {
std::vector<std::pair<int, int>> points;
for (int X = 0; X < window.getSize().x; X += 10) {
for (int Y = 0; Y < window.getSize().y; Y += 10) {
points.emplace_back(X, Y);
}
}
return points;
}
template<typename F>
auto filter(F f) {
return [f](const std::pair<int, int> & point) {
auto x = static_cast<float>(point.first);
auto y = static_cast<float>(point.second);
x = x / 2800.f * 4 + 0 - 4 / 2.f;
y = -((1800.f - y) / 1800 * 4 + 0 - 4 / 2.f);
return (func(x, y, 1, 2, 3) > 1);
}
}
sf::CircleShape toCircle(int X, int Y) {
sf::CircleShape circle(20);
circle.setPointCount(3);
circle.setFillColor(sf::Color::Cyan);
circle.setPosition(X, Y);
return circle;
}
template <typename F>
void drawCircles(sf::RenderWindow& window, F f) {
auto points = getPoints(window);
auto end = std::remove_if(std::execution::par, points.begin(), points.end(), filter(f));
points.erase(end, points.end());
for (auto & [X, Y] : points) {
window.draw(toCircle(X, Y));
}
}
float func(float x, float y, float a, float b, float r) {
return 1.f / sqrt((x - a)*(x - a) + (y - b)*(x - b));
}
int main () {
sf::RenderWindow window(sf::VideoMode(2800, 1800), "");
window.setFramerateLimit(20);
sf::Event event{};
threader tf(window);
while (window.isOpen()) {
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed: {
window.close();
}
}
}
window.clear();
drawCircles(window, func);
window.display();
}
}
There is a problem with trying to render from multiple threads without properly switching GL contexts.
Since you are doing this on the CPU, here are some options:
Make multiple sf::RenderTexture objects, render to each one of them in a separate thread (don't forget to call display() at the end), then draw them on the main thread in some sprites. (harder and not sure it is the way to go, but I don't know what you want to do in the end)
Make multiple sf::Image objects, render to each one of them in a separate thread, then draw them on the main thread in some sprites.
Make just one sf::Image object, each thread renders in a separate area of the image, then draw the image on the main thread in some sprite.
options 2 & 3 make it more easy to work with at a pixel level, also faster. Make sure you traverse the image in a cache friendly way (line by line most likely)
I would go with 2 or 3 depending on how you think it's better for your application.
Also, use sf::Transform for the transformation from screen space (Image space) to world space where the metaballs live.
https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Image.php
https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Transform.php#ab42a0bb7a252c6d221004f6372ce5fdc
I am working on an ledstip project for my room. I am using arduino to do this. For this project i want to use C++ so i can use OOP. After i got my ledstrips working I wanted to create a cluster class that used the strip class to control a specific piece of the LED strip. I can't get this to work. The compiler gives no errors and I see no change after using the function Desk.rgb(0,100,0);.
Here is my .h file
#include <FastLED.h>
template<class T>
class Cluster {
public:
T Strip;
int first;
int last;
Cluster(T Strip, int first, int last) {
this->Strip = Strip;
this->first = first;
this->last = last;
}
void rgb(int r, int g, int b){
Strip.rgb( r, g, b, first, last);
}
};
template<byte pin, int AmountOfLeds>
class Strip {
public:
CRGB leds[AmountOfLeds];
void setup() {
FastLED.addLeds<WS2812, pin, GRB>(leds, AmountOfLeds);
rgb(0, 0, 0);
}
//hole strip
void rgb(int r, int g, int b) {
for (int i = 0; i <= AmountOfLeds - 1; i++) {
this->leds[i] = CRGB(r, g, b);
}
FastLED.show();
}
//single led
void rgb(int i, int r, int g, int b) {
this->leds[i] = CRGB(r, g, b);
FastLED.show();
}
//range
void rgb(int r, int g, int b, int f, int l) {
for (int i = f; i <= l; i++) {
this->leds[i] = CRGB(r, g, b);
}
FastLED.show();
}
void hsv(int h, int s, int v) {
for (int i = 0; i <= AmountOfLeds; i++) {
this->leds[i] = CHSV(h, s, v);
}
FastLED.show();
}
void hsv(int i, int h, int s, int v) {
this->leds[i] = CHSV(h, s, v);
FastLED.show();
}
void hsv(int h, int s, int v, int f, int l) {
for (int i = f; i <= l; i++) {
this->leds[i] = CHSV(h, s, v);
}
FastLED.show();
}
void hsvWhiteBalance(int S, int V) { //S is yellowness, V is brightness
hsv(15, S, V);
}
void rainbow(float V) {
for (int i = 0; i <= AmountOfLeds; i++) {
leds[i] = CHSV( float(i) * (255 / float(AmountOfLeds)), 255, V);
}
FastLED.show();
}
void rainbow(float p, float V) {
for (int i = 0; i <= AmountOfLeds; i++) {
leds[i] = CHSV( float(i) * (255.0 / float(AmountOfLeds) * p), 255, V);
}
FastLED.show();
}
};
This is my .ino file:
#include "LedClasses.h"
Strip<5, 190> DTVC;
Cluster<Strip<5, 190>> Desk(DTVC, 1, 42);
void setup() {
Serial.begin(9600);
DTVC.setup();
DTVC.hsvWhiteBalance(153, 50);
Desk.rgb(0,100,0);
//DTVC.rgb(Desk, 0, 100, 0);
}
Thanks in advance.
Doesn't work, because in the Cluster constructor you take Strip class by copy. Then, in your example you have 2 instances of Stripe: one in the global context, and one inside the Cluster. You invoke Stripe::setup (which calls FastLED.addLeds) on the instance in the global context (registering the address of the Stripe::ledspublic field in the FastLED library), but later you call rgb on the instance that lives inside your Cluster and has different Stripe::leds field.
To fix it quickly (not very clean, though), you can redesign your constructor to accept a reference instead of copy:
class Cluster {
public:
T& strip;
int first;
int last;
Cluster(T& s, int f, int l): strip(s), first(f), last(l) {}
Alternatively, you can redesign your architecture a bit more, to not use the templates so much (you can play with constexpr arguments instead).
When I create a function such as:
int addThree(int x=1, int y=1, int z=1)
I want to call the function such that it uses the default arguments for x and z, but not y.
Some attempts have been addThree(5,,5) and addThree(5,NULL,5), but neither work effectively.
The default arguments must be at the last of your list, so do as follows
int addThree(int y , int x = 1, int z = 1)
{
//some stuff
return someInt;
}
, hence you can call it as
int ans = addThree(4);
Default arguments in C++, need to be specified in immediate succession, and cannot be succeeded by a non-default parameter.
So, something like
int sum(int x = 0, int y, int z = 0) {
return (x + y + z);
}
is forbidden in C++
The function needs to be as follows:
#include <iostream>
int sum(int x, int y = 0, int z = 0) {
return (x + y + z);
}
int main() {
std::cout << sum(1) << "\n";//calls sum(1,0,0)
std::cout << sum(1,2) << "\n";//calls sum(1,2,0)
return 0;
}
However, while specifying default arguments, you always need to take care in function overloading. The overloaded functions cannot be called ambiguously..
So a code like:
#include <iostream>
int sum(int x, int y = 0, int z = 0) {
return (x + y + z);
}
float sum(int x, float y = 0.0, float z = 0.0) {
return (float(x) + y + z);
}
int main() {
std::cout << sum(1) << "\n";
return 0;
}
does not compile and righty produces ambiguity error, as the compiler does not understand
Whether it should call the first sum, or the second sum.
If you're consistently passing a value for one parameter and using the defaults for the others, you can rearrange the parameters to the one you need to pass is first, and the ones for which you use defaults come later. But that only works if it's essentially always the same ones for which you supply a value vs. use the defaults.
Otherwise, if you need something similar to the basic capability badly enough, you can pass an instance of a class, and have that class implement the named parameter idiom.
class triplet {
int x_ {1};
int y_ {1};
int z_ {1};
public:
triplet &x(int val) { x_ = val; return *this; }
triplet &y(int val) { y_ = val; return *this; }
triplet &z(int val) { z_ = val; return *this; }
int x() const { return x_; }
int y() const { return y_; }
int z() const { return z_; }
};
int addThree(triplet const &t) {
return oldAddThree(t.x(), t.y(), t.z());
}
int ans = addThree(triplet().x(4));
This lets you use the defaults for as many or few of the values you need as you want, and override only those that you actually want to. On the other hand, it does add a fair amount of syntactic overhead, so you have to want the capability pretty badly to bother.
I feel that its unlikelier than not, but I'd like to see if a function can deduce its parameters from a trivially wrapped struct. For example:
struct wrapped_float
{
float f;
wrapped_float(float f) : f(f) {}
};
float saxpy(float a, float x, float y)
{
return a * x + y;
}
int main()
{
wrapped_float a = 1.1, x = 2.2, y = 3.3;
auto result = saxpy(a, x, y); // ofc compile error
}
The motivation behind this is to make a lightweight wrapper around GDI calls with device context handles (HDC). There exists a lot of legacy code which uses HDCs and I'd like to refactor a lot of this code incrementally. My strategy is to make a lightweight wrapper around HDC like this:
#include <Windows.h>
struct graphics
{
HDC dc;
graphics(HDC dc) : dc(dc) {}
void rectangle(int x, int y, int w, int h)
{
Rectangle(dc, x, y, x + w, y + h);
}
};
void OnPaint(HDC dc)
{
Rectangle(dc, 1, 2, 3, 4);
}
int main()
{
HDC dc;
// setup dc here
graphics g = dc;
OnPaint(g);
}
So that if g can be implicitly transformed to HDC, then all legacy code will normally compile, but I can slowly refactor code to become like this:
void OnPaint(graphics g)
{
g.rectangle(1, 2, 3, 4);
}
Any recommendations are also welcome since this simply might not be possible in C++ (or any programming language).
From the comments, I was not aware that C++ had a casting operator. The simple solution is to add:
struct graphics
{
HDC dc;
graphics(HDC dc) : dc(dc) {}
void rectangle(int x, int y, int w, int h)
{
Rectangle(dc, x, y, x + w, y + h);
}
operator HDC()
{
return dc;
}
};
OK so I am working on some game logic, I have done a fair bit of research (as much as the internet will allow) and still don't have a solid understanding of class and struct so please go gentle!
Basically, I want to be able to create an object with the properties all on one line ie.
object a{1, 1, 50, 15, 5}; // create object a
and I want some extra stuff to be made up aswell like:
class object
{
public:
int x;
int y;
int h;
int w;
int s;
int x1;
int y1;
int ps;
int ns;
int x1 = x + w;
int y1 = y + h;
int ps = 0 + s;
int ns = 0 - s;
};
I don't know which language you're working with, but it looks a bit like C++, so here's an example:
class Rect
{
public:
int x, y;
int w, h;
int right, bottom;
// This method is called a constructor.
// It allows you to perform tasks on
// the instantiation of an object.
Rect(int x_, int y_, int w_, int h_)
{
// store geometry
this->x = x_;
this->y = y_;
this->w = w_;
this->h = h_;
// calculate sides
this->right = x_ + w_;
this->bottom = y_ + h_;
}
};
// You use the constructor in your main() function like so:
Rect myObject(1, 1, 50, 15);
// And you can access the members like so:
myObject.x = 10;
myObject.right = myObject.x + myObject.w;
You cannot use operators inside the definition of a class as you proposed in your question. Operations on variables must take place inside a constructor (or other method).