1

I have a Player object which can throw an exception inside its constructor, so in my main function I'm creating 2 Player objects inside a try block. I want to store these 2 Players in a std::array like that:

try
{
  Player p1(10, 10, ikazuchi);
  Player p2(70, 10, hibiki);
  std::array<Player, 2> players = {p1, p2};
}

The problem is that I wouldn't be able to use the array outside of the try block, and I heard that placing all the main code inside a try block is often a bad idea.

I can't declare my std::array after the try block because p1 and p2 no longer exist there.

I can solve the problem with a std::vector, but I have read that it was better to use a std::array when I know the size of the array during the compilation.

I could create a default constructor to create my objects then fill them inside the try block, but it seems to be more proper to create everything in the constructor.

What would be the best practice for that?

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
Xwilarg
  • 364
  • 1
  • 6
  • 16
  • What error are you trying to catch? – Tatsuyuki Ishi Apr 14 '17 at 12:04
  • Wait can you reexplain or reword why you don't declare your array outside of the try block? because you can declare it outside of the try block, and store your players in it inside of the try block... – AresCaelum Apr 14 '17 at 12:05
  • Is `Player` noexcept movable ? – Jarod42 Apr 14 '17 at 12:13
  • Put the whole of `main` in a `try` block. It is not bad practice (I would recommend it). But the point is that your `std::array` is unusable if you can not create both your players. So it should go out of scope if it can't be initialized. – Galik Apr 14 '17 at 12:18
  • @TatsuyukiIshi I throw exception when I'm trying to load the textures for the player but the function return NULL. – Xwilarg Apr 14 '17 at 12:33
  • @Eddge I didn't have a default constructor, but thinking about it, it's maybe better to make one. – Xwilarg Apr 14 '17 at 12:41

3 Answers3

2

You can do something like below.

int main() {
  std::array<int, 2> array;
  try {
    array[0]=1;
    array[1]=1;
  } catch (...) {
  }
  return 0;
}

Use Player instead of int;

Manthan Tilva
  • 3,135
  • 2
  • 17
  • 41
  • 1
    I tried to do it but I have the following error since it's in the try bloc: use of deleted function ‘std::array::array()’ – Xwilarg Apr 14 '17 at 12:08
  • @Xwilarg if you want to use p1 and p2 outside try block then you need to use pointer of Player instead of object of Player. – Manthan Tilva Apr 14 '17 at 12:10
  • 1
    @ManthanTilva not necessarily, using when you make an assignment as you did it will make a copy of it in the array. – AresCaelum Apr 14 '17 at 12:11
  • The question implies `Player` doesn't have a default constructor. – Angew is no longer proud of SO Apr 14 '17 at 12:11
  • The question implies he didnt make one, that doesn't mean there isn't one unless he implicitly marked it as deleted or made it private it still has a default constructor, it just doesnt set the values. – AresCaelum Apr 14 '17 at 12:12
  • @Eddge Wrong. As soon as you have a user-provided ctor, no deault one is generated. – Angew is no longer proud of SO Apr 14 '17 at 12:13
  • @Angew http://stackoverflow.com/questions/563221/is-there-an-implicit-default-constructor-in-c – AresCaelum Apr 14 '17 at 12:14
  • @Eddge Yes, exactly. "If you do not define **a constructor**, the compiler will define a default constructor for you." Not "if you do not define a **default** constructor." **Any** user-provided ctor inhibits the default ctor. [See also here](http://coliru.stacked-crooked.com/a/0e2c1997fcdd1d1b) – Angew is no longer proud of SO Apr 14 '17 at 12:16
  • @Angew just tested it myself inside a test case, you are correct, I could have sworn it was the opposite. – AresCaelum Apr 14 '17 at 12:18
  • @Eddge Think about the logic of it. If there's a user-defined ctor, it probably means the class has invariants to maintain. The default ctor would most likely not establish such invariants and thus be wrong. Since C++11, you can request the default ctor using `X() = default;` even if you provide other ctors, if the deafult behaviour is fine for you. – Angew is no longer proud of SO Apr 14 '17 at 12:20
  • @Eddge How to test this? Can you share your test case? – Manthan Tilva Apr 14 '17 at 12:21
  • @ManthanTilva I linked to a test case in my earlier comment ("See also here") – Angew is no longer proud of SO Apr 14 '17 at 12:21
  • @Angew indeed, that's why I said it doesn't set anything which made sense to me at the time. I appreciate you correcting me, and preventing the spread of false information thanks. – AresCaelum Apr 14 '17 at 12:23
  • I tried to do that, but then I have problems to access to the elements of my object. – Xwilarg Apr 14 '17 at 13:35
2

You can always get around issues like this using dynamic allocation or something like boost::optional<std::array<Player, 2>>, but the real question is: should you? That is, assume one of the player objects fails to construct and throws an exception. What will you do with the players array after that? It's not in a legal state, or at least not in the state you'd expect it to be if no exception was thrown.

There is no problem with having the scope of a try be large. It should cover everything where an exception will prevent you from progressing. If you don't like physically having a lot of source code there, move the code into a function. This might be a good idea anyway: one function which assumes all goes well, and another one whose chief responsibility is to handle errors.

And if you have meaningful ways of continuing to use players when the objects inside are in an invalid state (constructor threw), then you can just create the array with objects in that state in the first place (outside the try block), and just assign them inside the try.

The question states you don't want to provide an unneeded default constructor, but I claim that either player belongs inside the try, or Player needs a default constructor (or an equivalent way of expressing "not initialised properly).

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • Isn't it a problem if the try block cover my whole code ? Since my Player objects will be used in my event loop... Is it better to don't catch the exception ? – Xwilarg Apr 14 '17 at 12:13
  • 3
    @Xwilarg You can't think in terms of sweeping rules like "whole code in `try` is bad." Those aren't dogmas. You have to understand them, and understand your code. If your code cannot function without the objects, simply catch & log any exceptions in creating them, and then exit gracefully. – Angew is no longer proud of SO Apr 14 '17 at 12:15
0

If your players csn be copied/moved and trivially constructed, just create the array and assign into it.

std::array<Player, 2> players;
try{
  players[0]=Player(10, 10, ikazuchi);
  players[1]=Player(70, 10, hibiki);
} catch ...

alternatively you can do:

std::optional<std::array<Player, 2>> players;
try{
  Player p1(10, 10, ikazuchi);
  Player p2(70, 10, hibiki);
  players.emplace(std::array<Player, 2>{{std::move(p1),std::move(p2)}});
} catch ...

but you may need to find a non-C++17 version of optional, like boost optional.

Another approach is:

auto players = [&]()->std::array<Player, 2>{
  try {
    Player p1(10, 10, ikazuchi);
    Player p2(70, 10, hibiki);
    return {{std::move(p1),std::move(p2)}};
  } catch (some_error){
    throw some_other_error;
  }
}();

but thus requires exit either by array or by throw.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524