From ec158b3dcb9191385188a2bc2446594989590d8a Mon Sep 17 00:00:00 2001 From: godardma Date: Mon, 30 Sep 2024 11:00:18 +0200 Subject: [PATCH 1/2] [C++ API] shared_ptr to avoid memory leaks --- client-api/C++/src/vibes.cpp | 218 ++++++++++++++++++++++------------- client-api/C++/src/vibes.h | 6 +- 2 files changed, 142 insertions(+), 82 deletions(-) diff --git a/client-api/C++/src/vibes.cpp b/client-api/C++/src/vibes.cpp index 7268e64..764999c 100644 --- a/client-api/C++/src/vibes.cpp +++ b/client-api/C++/src/vibes.cpp @@ -28,6 +28,8 @@ #include #include #include +#include + // // Vibes properties key,value system implementation // @@ -93,7 +95,8 @@ namespace vibes namespace { /// Current communication file descriptor - FILE *channel=0; + // FILE *channel=nullptr; + std::shared_ptr channel; /// Current figure name (client-maintained state) string current_fig="default"; @@ -124,59 +127,79 @@ namespace vibes void beginDrawing(const std::string &fileName) { - channel=fopen(fileName.c_str(),"a"); + if (!channel) + channel = std::shared_ptr(fopen(fileName.c_str(),"a"), [](FILE* file) { + if (file) { + fclose(file); + } + }); + } + + void beginDrawingIfNeeded() + { + if (!channel) + { + beginDrawing(); + } } void endDrawing() { - fclose(channel); + } + + // // Figure management // void newFigure(const std::string &figureName) { + beginDrawingIfNeeded(); std::string msg; if (!figureName.empty()) current_fig = figureName; msg ="{\"action\":\"new\"," "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"}\n\n"; - fputs(msg.c_str(),channel); - fflush(channel); + fputs(msg.c_str(),channel.get()); + fflush(channel.get()); } void clearFigure(const std::string &figureName) { + beginDrawingIfNeeded(); std::string msg; msg="{\"action\":\"clear\"," "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"}\n\n"; - fputs(msg.c_str(),channel); - fflush(channel); + fputs(msg.c_str(),channel.get()); + fflush(channel.get()); } void closeFigure(const std::string &figureName) { + beginDrawingIfNeeded(); std::string msg; msg="{\"action\":\"close\"," "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"}\n\n"; - fputs(msg.c_str(),channel); - fflush(channel); + fputs(msg.c_str(),channel.get()); + fflush(channel.get()); } void saveImage(const std::string &fileName, const std::string &figureName) { - std::string msg; - msg="{\"action\":\"export\"," - "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"," - "\"file\":\""+fileName+"\"}\n\n"; - fputs(msg.c_str(),channel); - fflush(channel); + beginDrawingIfNeeded(); + std::string msg; + msg="{\"action\":\"export\"," + "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"," + "\"file\":\""+fileName+"\"}\n\n"; + fputs(msg.c_str(),channel.get()); + fflush(channel.get()); } void selectFigure(const std::string &figureName) { - current_fig = figureName; + beginDrawingIfNeeded(); + current_fig = figureName; } @@ -186,17 +209,20 @@ namespace vibes void axisAuto(const std::string &figureName) { + beginDrawingIfNeeded(); setFigureProperty(figureName.empty()?current_fig:figureName, "viewbox", "auto"); } void axisLimits(const double &x_lb, const double &x_ub, const double &y_lb, const double &y_ub, const std::string &figureName) { + beginDrawingIfNeeded(); Vec4d v4d = { x_lb, x_ub, y_lb, y_ub }; setFigureProperty(figureName.empty()?current_fig:figureName, "viewbox", v4d); } void axisLabels(const std::string &x_label, const std::string &y_label, const std::string &figureName) { + beginDrawingIfNeeded(); vector labels; labels.push_back(x_label); labels.push_back(y_label); @@ -205,6 +231,7 @@ namespace vibes void axisLabels(const std::vector &labels, const std::string &figureName) { + beginDrawingIfNeeded(); setFigureProperty( figureName.empty()?current_fig:figureName, "axislabels", labels); } @@ -215,18 +242,20 @@ namespace vibes void drawBox(const double &x_lb, const double &x_ub, const double &y_lb, const double &y_ub, Params params) { + beginDrawingIfNeeded(); Vec4d v4d = { x_lb, x_ub, y_lb, y_ub }; Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "box", "bounds", v4d); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawBox(const vector &bounds, Params params) { + beginDrawingIfNeeded(); assert(!bounds.empty()); assert(bounds.size()%2 == 0); @@ -235,13 +264,14 @@ namespace vibes msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "box", "bounds", vector(bounds.begin(),bounds.end())); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawEllipse(const double &cx, const double &cy, const double &a, const double &b, const double &rot, Params params) { + beginDrawingIfNeeded(); Vec2d vc = { cx, cy }; Vec2d va = {a, b}; Params msg; @@ -252,14 +282,15 @@ namespace vibes "axis", va, "orientation", rot); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawConfidenceEllipse(const double &cx, const double &cy, const double &sxx, const double &sxy, const double &syy, const double &K, Params params) { + beginDrawingIfNeeded(); Vec2d vc = {cx, cy}; Vec4d vcov = { sxx, sxy, sxy, syy }; Params msg; @@ -270,13 +301,14 @@ namespace vibes "covariance", vcov, "sigma", K); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawConfidenceEllipse(const vector ¢er, const vector &cov, const double &K, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); @@ -285,13 +317,14 @@ namespace vibes "covariance", vector(cov.begin(),cov.end()), "sigma", K); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawSector(const double &cx, const double &cy, const double &a, const double &b, const double &startAngle, const double &endAngle, Params params) { + beginDrawingIfNeeded(); // Angle need to be in degree Params msg; Vec2d cxy={ cx, cy }; @@ -305,13 +338,14 @@ namespace vibes "orientation", 0, "angles", startEnd); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawPie(const double &cx, const double &cy, const double &r_min, const double &r_max, const double &theta_min, const double &theta_max, Params params) { + beginDrawingIfNeeded(); // Angle need to be in degree Params msg; Vec2d cxy = { cx, cy }; @@ -324,36 +358,39 @@ namespace vibes "rho", rMinMax, "theta", thetaMinMax); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawPoint(const double &cx, const double &cy, Params params) { + beginDrawingIfNeeded(); Params msg; Vec2d cxy = { cx, cy }; msg["action"]="draw"; msg["figure"]=params.pop("figure",current_fig); msg["shape"]=(params, "type","point", "point",cxy); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawPoint(const double &cx, const double &cy, const double &radius, Params params) { + beginDrawingIfNeeded(); Params msg; Vec2d cxy = { cx, cy }; msg["action"]="draw"; msg["figure"]=params.pop("figure",current_fig); msg["shape"]=(params, "type","point", "point",cxy,"Radius",radius); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawRing(const double &cx, const double &cy, const double &r_min, const double &r_max, Params params) { + beginDrawingIfNeeded(); Params msg; Vec2d cxy = { cx, cy }; Vec2d rMinMax = { r_min, r_max }; @@ -362,48 +399,52 @@ namespace vibes msg["shape"] = (params, "type", "ring", "center", cxy, "rho", rMinMax); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawBoxes(const std::vector > &bounds, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "boxes", "bounds", bounds); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawBoxesUnion(const std::vector > &bounds, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "boxes union", "bounds", bounds); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawLine(const std::vector > &points, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "line", "points", points); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawLine(const std::vector &x, const std::vector &y, Params params) { + beginDrawingIfNeeded(); // Reshape x and y into a vector of points std::vector points; std::vector::const_iterator itx = x.begin(); @@ -421,8 +462,8 @@ namespace vibes msg["shape"] = (params, "type", "line", "points", points); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } //void drawPoints(const std::vector > &points, Params params) @@ -432,8 +473,8 @@ namespace vibes // msg["figure"] = params.pop("figure",current_fig); // msg["shape"] = (params, "type", "points", // "points", points); - // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - // fflush(channel); + // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + // fflush(channel.get()); //} //void drawPoints(const std::vector > &points, const std::vector &colorLevels, const std::vector &radiuses, Params params) @@ -445,12 +486,13 @@ namespace vibes // "points", points, // "colorLevels", colorLevels, // "radiuses", radiuses); - // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - // fflush(channel); + // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + // fflush(channel.get()); //} void drawPoints(const std::vector &x, const std::vector &y, Params params) { + beginDrawingIfNeeded(); // Reshape x and y into a vector of points std::vector points; std::vector::const_iterator itx = x.begin(); @@ -468,8 +510,8 @@ namespace vibes msg["shape"] = (params, "type", "points", "centers", points); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } //void drawPoints(const std::vector &x, const std::vector y, const std::vector &colorLevels, Params params) @@ -492,8 +534,8 @@ namespace vibes // "points", points, // "colorLevels", colorLevels); // - // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - // fflush(channel); + // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + // fflush(channel.get()); //} //void drawPoints(const std::vector &x, const std::vector y, const std::vector &colorLevels, const std::vector &radiuses, Params params) @@ -517,12 +559,13 @@ namespace vibes // "colorLevels", colorLevels, // "radiuses", radiuses); // - // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - // fflush(channel); + // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + // fflush(channel.get()); //} void drawArrow(const double &xA, const double &yA, const double &xB, const double &yB, const double &tip_length, Params params) { + beginDrawingIfNeeded(); // Reshape A and B into a vector of points std::vector points; Vec2d va = { xA, yA }; @@ -538,12 +581,13 @@ namespace vibes "points", points, "tip_length", tip_length); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawArrow(const std::vector > &points, const double &tip_length, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); @@ -551,12 +595,13 @@ namespace vibes "points", points, "tip_length", tip_length); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawArrow(const std::vector &x, const std::vector &y, const double &tip_length, Params params) { + beginDrawingIfNeeded(); // Reshape x and y into a vector of points std::vector points; std::vector::const_iterator itx = x.begin(); @@ -575,12 +620,13 @@ namespace vibes "points", points, "tip_length", tip_length); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawPolygon(const std::vector &x, const std::vector &y, Params params) { + beginDrawingIfNeeded(); // Reshape x and y into a vector of points std::vector points; std::vector::const_iterator itx = x.begin(); @@ -598,12 +644,13 @@ namespace vibes msg["shape"] = (params, "type", "polygon", "bounds", points); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawVehicle(const double &cx, const double &cy, const double &rot, const double &length, Params params) { + beginDrawingIfNeeded(); Vec2d vc = { cx, cy }; Params msg; msg["action"] = "draw"; @@ -613,12 +660,13 @@ namespace vibes "length", length, "orientation", rot); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawAUV(const double &cx, const double &cy, const double &rot, const double &length, Params params) { + beginDrawingIfNeeded(); Vec2d vc = { cx, cy }; Params msg; msg["action"] = "draw"; @@ -628,12 +676,13 @@ namespace vibes "length", length, "orientation", rot); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawTank(const double &cx, const double &cy, const double &rot, const double &length, Params params) { + beginDrawingIfNeeded(); Vec2d vc = { cx, cy }; Params msg; msg["action"] = "draw"; @@ -643,12 +692,13 @@ namespace vibes "length", length, "orientation", rot); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawRaster(const std::string& rasterFilename, const double &xlb, const double &yub, const double &xres, const double &yres, Params params) { + beginDrawingIfNeeded(); Vec2d ul_corner = { xlb, yub }; Vec2d scale = { xres, yres }; @@ -661,13 +711,14 @@ namespace vibes "scale", scale ); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void newGroup(const std::string &name, Params params) { + beginDrawingIfNeeded(); // Send message Params msg; msg["action"] = "draw"; @@ -675,19 +726,20 @@ namespace vibes msg["shape"] = (params, "type", "group", "name", name); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void clearGroup(const std::string &figureName, const std::string &groupName) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "clear"; msg["figure"] = figureName; msg["group"] = groupName; - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void clearGroup(const std::string &groupName) @@ -698,13 +750,14 @@ namespace vibes void removeObject(const std::string &figureName, const std::string &objectName) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "delete"; msg["figure"] = figureName; msg["object"] = objectName; - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void removeObject(const std::string &objectName) @@ -715,23 +768,26 @@ namespace vibes // Property modification void setFigureProperties(const std::string &figureName, const Params &properties) { + beginDrawingIfNeeded(); // Send message Params msg; msg["action"] = "set"; msg["figure"] = figureName; msg["properties"] = properties; - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void setFigureProperties(const Params &properties) { + beginDrawingIfNeeded(); setFigureProperties(current_fig, properties); } void setObjectProperties(const std::string &figureName, const std::string &objectName, const Params &properties) { + beginDrawingIfNeeded(); // Send message Params msg; msg["action"] = "set"; @@ -739,12 +795,12 @@ namespace vibes msg["object"] = objectName; msg["properties"] = properties; - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void setObjectProperties(const std::string &objectName, const Params &properties) { setObjectProperties(current_fig, objectName, properties); } -} +} \ No newline at end of file diff --git a/client-api/C++/src/vibes.h b/client-api/C++/src/vibes.h index 2dc6dd5..9d5ee02 100644 --- a/client-api/C++/src/vibes.h +++ b/client-api/C++/src/vibes.h @@ -221,9 +221,13 @@ namespace vibes { /// Start VIBes in file saving mode. All commands are saved to the specified file. void beginDrawing(const std::string &fileName); + /// Start VIBes in connected mode: connects to the VIBes viewer if needed. + void beginDrawingIfNeeded(); + /// Close connection to the viewer or the drawing file. void endDrawing(); + /** @} */ // end of group connection @@ -537,4 +541,4 @@ namespace vibes { #define vibesDrawCircle(cx,cy,r,...) vibes::drawCircle(cx,cy,r,vibesParams(__VA_ARGS__)) #endif //#ifdef VIBES_GENERATE_vibesXXX_MACROS -#endif //#ifndef VIBES_CPP_API_H +#endif //#ifndef VIBES_CPP_API_H \ No newline at end of file From f56cca89bdca2a6b026846d24fd22d4763596c37 Mon Sep 17 00:00:00 2001 From: Simon Rohou Date: Tue, 1 Oct 2024 15:08:06 +0200 Subject: [PATCH 2/2] [C++ API] added comments --- client-api/C++/examples/all_commands.cpp | 4 ++-- client-api/C++/examples/pong.cpp | 2 +- client-api/C++/examples/sivia_simple.cpp | 4 ++-- client-api/C++/src/vibes.cpp | 10 +++++++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/client-api/C++/examples/all_commands.cpp b/client-api/C++/examples/all_commands.cpp index 7f6893e..c179693 100644 --- a/client-api/C++/examples/all_commands.cpp +++ b/client-api/C++/examples/all_commands.cpp @@ -54,7 +54,7 @@ int main() "| VIBES C++ API commands test |" "\n" "-------------------------------" << endl; - VIBES_TEST( vibes::beginDrawing() ); + // Not needed anymore: VIBES_TEST( vibes::beginDrawing() ); cout << "Figure creation function" << endl; VIBES_TEST( vibes::newFigure() ); @@ -303,7 +303,7 @@ int main() // vibes::drawRaster("./raster_example.png", 10, 5, 0.1, 0.1); std::cout << "end drawing" << std::endl; - VIBES_TEST( vibes::endDrawing() ); + // Not needed anymore: VIBES_TEST( vibes::endDrawing() ); // Testing Vibes params system cout << "Testing Vibes params system" << endl; diff --git a/client-api/C++/examples/pong.cpp b/client-api/C++/examples/pong.cpp index 31c15cc..3c871ed 100644 --- a/client-api/C++/examples/pong.cpp +++ b/client-api/C++/examples/pong.cpp @@ -45,7 +45,7 @@ float angle; int main() { - vibes::beginDrawing(); + // Not needed anymore: vibes::beginDrawing(); initScene(); while(true) updateScene(); diff --git a/client-api/C++/examples/sivia_simple.cpp b/client-api/C++/examples/sivia_simple.cpp index 35d52bd..4ceaa75 100644 --- a/client-api/C++/examples/sivia_simple.cpp +++ b/client-api/C++/examples/sivia_simple.cpp @@ -35,7 +35,7 @@ interval dist(box robot, box landmark) int main() { - vibes::beginDrawing(); // <== Initializes the VIBES "connection" + // Not needed anymore: vibes::beginDrawing(); // <== Initializes the VIBES "connection" vibes::newFigure("SIVIA"); // <== Create a new VIBes figure box robot(interval(-10,10),interval(-10,10)); @@ -66,6 +66,6 @@ int main() } } - vibes::endDrawing(); // <== closes the VIBES "connection" + // Not needed anymore: vibes::endDrawing(); // <== closes the VIBES "connection" return 0; } diff --git a/client-api/C++/src/vibes.cpp b/client-api/C++/src/vibes.cpp index 764999c..4360d5c 100644 --- a/client-api/C++/src/vibes.cpp +++ b/client-api/C++/src/vibes.cpp @@ -128,11 +128,15 @@ namespace vibes void beginDrawing(const std::string &fileName) { if (!channel) - channel = std::shared_ptr(fopen(fileName.c_str(),"a"), [](FILE* file) { - if (file) { + channel = std::shared_ptr( + fopen(fileName.c_str(),"a"), + // Callback for automatically closing the file + // when the shared_ptr is released: + [](FILE* file) { + if(file) fclose(file); } - }); + ); } void beginDrawingIfNeeded()