debugcppMinor
Game framework using C++
Viewed 0 times
gameusingframework
Problem
I'm writing my own game framework and would like to get feedback on the API while I'm writing it. At the moment it's very simple, but I would like some guidance about where to take it.
This is a sample main function to create a window:
Here is the declaration of MethodResult (it's a header-only class)
```
#pragma once
#include
#include
#include
namespace pneu {
namespace core {
class MethodResult final {
public:
static auto ok(void) -> MethodResult
{
return MethodResult(true, "");
}
static auto error(const std::string& desc) -> MethodResult
{
return MethodResult(false, desc);
}
MethodResult(const MethodResult&) = default;
~MethodResult(void) = default;
inline auto isOk(void) const -> bool
{
return fOk;
}
inline auto getError(void) const -> std::string
{
return fDescription;
}
inline auto onError(const std::function& f) -> void
{
if (!isOk()) {
f(getError());
}
}
inline auto throwOnError(const std::exception& e) -> void
{
if (!isOk()) {
throw e;
}
}
private:
MethodResult(bool ok, const std::string& desc)
:
fOk(ok),
fDescription(desc) { }
const bool fOk;
const std::string fDescription;
};
} // namespace core
} // namespace pneu
#define PNEU_EXCEPT_TO_METHODRES(func) \
do { \
try { \
func; \
} catch(const std::exception& e) { \
ret
This is a sample main function to create a window:
#include
#include
#include "pneu/graphics/Window.hpp"
#include "pneu/core/MethodResult.hpp"
int main(int argc, const char** argv) {
// declare window
pneu::graphics::Window window("hello-world", 800, 600, 80, 60);
// initialise it, handling any errors
window.init().onError([](const std::string& error) {
std::cout << error << std::endl;
exit(1);
});
// main event loop
while (window.isRunning()) {
window.pollEvents();
window.update();
window.renderFrame();
}
return 0;
}Here is the declaration of MethodResult (it's a header-only class)
```
#pragma once
#include
#include
#include
namespace pneu {
namespace core {
class MethodResult final {
public:
static auto ok(void) -> MethodResult
{
return MethodResult(true, "");
}
static auto error(const std::string& desc) -> MethodResult
{
return MethodResult(false, desc);
}
MethodResult(const MethodResult&) = default;
~MethodResult(void) = default;
inline auto isOk(void) const -> bool
{
return fOk;
}
inline auto getError(void) const -> std::string
{
return fDescription;
}
inline auto onError(const std::function& f) -> void
{
if (!isOk()) {
f(getError());
}
}
inline auto throwOnError(const std::exception& e) -> void
{
if (!isOk()) {
throw e;
}
}
private:
MethodResult(bool ok, const std::string& desc)
:
fOk(ok),
fDescription(desc) { }
const bool fOk;
const std::string fDescription;
};
} // namespace core
} // namespace pneu
#define PNEU_EXCEPT_TO_METHODRES(func) \
do { \
try { \
func; \
} catch(const std::exception& e) { \
ret
Solution
There are several small things that you can easily improve:
-
Tell me if I am mistaken, but it seems that the macro
-
In
-
If you want to copy a
-
The function trailing
Note that I did not chose the best function to prove my point. However, this allows to split long declarations in two lines and still have both the function names and the return types aligned. Example with
The obvious drawback is of course the fact that the declaration now takes two lines. There is a choice to make, and it is yours.
-
You can use list initialization in a
-
Two small things concerning your
-
Tell me if I am mistaken, but it seems that the macro
PNEU_EXCEPT_TO_METHODRES needs some trailing while (0) if you don't want to add it manually every time you invoke it.-
In
MethodResult, you explicitly defaulted the copy constructor and the destructor. You could actually totally get rid of these lines by using the rule of zero: don't write any copy/move constructors/operators, and the compiler will generate them for. Unless you intend to make a non-copyable class or a RAII one, you generally want to use the rule of zero.-
If you want to copy a
string, don't pass it by const&, but use the copy-then-move idiom instead:MethodResult(bool ok, std::string desc)
:
fOk(ok),
fDescription(std::move(desc)) { }-
The function trailing
return type is fine, but if you want to get the readability benefits, you can put the type name on the following line. That really helps to visually separate the return type and avoids having overly long function declarations. That said, it is most useful when you have long template types.static auto ok()
-> MethodResult
{
return MethodResult(true, "");
}Note that I did not chose the best function to prove my point. However, this allows to split long declarations in two lines and still have both the function names and the return types aligned. Example with
Window class declaration:auto update()
-> void;
auto pollEvents()
-> void;
auto renderFrame()
-> void;The obvious drawback is of course the fact that the declaration now takes two lines. There is a choice to make, and it is yours.
-
You can use list initialization in a
return statement when you create and return an object and the same line. That will help avoiding to repeat the type:static auto ok()
-> MethodResult
{
return { true, "" };
}-
Two small things concerning your
main: don't bother to add argc and argv if you are not going to use them. You don't have to write return 0; at the end of main; if the compiler reaches the end of main without having encountered a return statement, it will automatically add a return 0;.Code Snippets
MethodResult(bool ok, std::string desc)
:
fOk(ok),
fDescription(std::move(desc)) { }static auto ok()
-> MethodResult
{
return MethodResult(true, "");
}auto update()
-> void;
auto pollEvents()
-> void;
auto renderFrame()
-> void;static auto ok()
-> MethodResult
{
return { true, "" };
}Context
StackExchange Code Review Q#59526, answer score: 2
Revisions (0)
No revisions yet.