Skip to main content
Became Hot Network Question
Deprettify the non-code
Source Link
Toby Speight
  • 88.7k
  • 14
  • 104
  • 327
$ g++ --version
g++ (Ubuntu 13.2.0-23ubuntu4) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -std=c++17 -Wall -Wextra ordered_char.cc test_main.cc -o test_main
$ g++ --version
g++ (Ubuntu 13.2.0-23ubuntu4) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -std=c++17 -Wall -Wextra ordered_char.cc test_main.cc -o test_main
$ g++ --version
g++ (Ubuntu 13.2.0-23ubuntu4) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -std=c++17 -Wall -Wextra ordered_char.cc test_main.cc -o test_main
$ g++ --version
g++ (Ubuntu 13.2.0-23ubuntu4) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -std=c++17 -Wall -Wextra ordered_char.cc test_main.cc -o test_main
Source Link

Implementation of an Ordered Character Class

Overview

The code below is my attempt a solving a problem that came up in one of my projects. I needed a type that could be constrained to a defined list of characters, be set to a specific character, and be able to be incremented / decremented to the following / proceeding character.

As I was writing the class I added some additional methods and behaviors that I expected to want / need in the future including the ability to either clamp or wrap-around when the end of the list of characters is reached.

There are three files included in this post:

  1. ordered_char.h
  2. ordered_char.cc
  3. test_main.cc

The first two define and implement the class and the third provides examples of usage and test cases.

Review Request

I would like to have this code reviewed for general feedback, naming of methods / variables, implementation of pre- and post- increment / decrement operators, am I making appropriate use of exceptions.

In particular I would like to focus on ordered_char.h and ordered_char.cc in this review.

I am also looking for feedback and suggestions for the areas of this code that I am not currently happy with:

  • I feel like there is too much code duplication in the implementation of the two constructors. I thought about using a delegated constructor, but I couldn't figure out how to do that properly without putting the implementation in the header.
  • In general I feel like there is too much repetition in the implementation when it comes to checking and implementing behavior at the ends of the provided character string.
  • The object is only valid if it has a non-empty list. This forced me to make the default list a single character. This seems like not the right behavior. I would like to force the objects to always be valid without needing to have a "non-sense" default value. I considered forcing one of the constructors to be used to create an object of this class, but then that led to a bunch of additional complexity such as not being able to declare a variable of this class type until I know what the contents of the class are.

Compiling the Code

I have compiled the code on Linux with the following command:

$ g++ --version
g++ (Ubuntu 13.2.0-23ubuntu4) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -std=c++17 -Wall -Wextra ordered_char.cc test_main.cc -o test_main

No warnings or errors are reported.

ordered_char.h

#ifndef H_BURNINGLOGIC_ORDERED_CHAR
#define H_BURNINGLOGIC_ORDERED_CHAR

/** \file ordered_char.h
    \brief Header for the OrderedChar class
*/

#include <cstddef>
#include <string>
#include <string_view>

namespace BurningLogic {

/** \class OrderedChar
  The OrderedChar class provides a character than can be incremented or decremented from a
  provided discrete ordered set of characters. The object can optionally clamp at the ends
  of the provided list or  wrap-around. It is an error to instantiate an object with an
  empty character list or one with characters that appear more then once.
*/
class OrderedChar {
 public:
   /**
   * Constructor for OrderedChar objects.  Requires specifing an ordered list of possible
   * characters.  If two paramaters are specified the second paramater determines if the 
   * values wrap-around at the extremes (clamp = false, the default) or are clamped to the
   * first or last value (clamp = true).  The default value of the OrederedChar object is
   * the first value of char_string (char_string[0]).
   * @param char_string a string containing, in order, the representable characters
   * @param clamp false for values to wrap around at the extremes or false to clamp at the extremes
   * @exception std::invalid_argument char_string is empty
   * @exception std::invalid_argument char_string contains duplicate characters
   */

  OrderedChar(std::string_view char_string, bool clamp = false);
  /**
   * Constructor for OrderedChar objects.  Requires specifing an ordered list of possible
   * characters.  If three paramaters are specified the second paramater specifies the inital
   * vale of the object and the third paramater determines if the values wrap-around at the
   * extremes (clamp = false, the default) or are clamped to the first or last value
   * (clamp = true).
   * @param char_string a string containing, in order, the representable characters
   * @param inital_char the inital value of the OrderedChar object
   * @param clamp false for values to wrap around at the extremes or false to clamp at the extremes
   * @exception std::invalid_argument char_string is empty
   * @exception std::invalid_argument char_string contains duplicate characters
   * @exception std::invalid_argument inital_char is not contained in char_string
   */
  OrderedChar(std::string_view char_string, char inital_char, bool clamp = false);

  /**
   * Returns the current value of the character.
   * @return the current value of the OrderedChar object
   */
  char GetChar(void) const;

  /**
   * Sets the current value to the specified character.  It is an error to
   * specify a character that is not part of the object's charater list.
   * @param c the character value to set the OrderedChar object to
   * @exception std::invalid_argument the character is not a memeber of the object's character list
   */
  void SetChar(char c);

  /**
   * Returns the predecessor to the object's current character value.  If the object is at the
   * beginning of the charatcer list and clamping is enabled it returns the first character in
   * the list.  If the object is at the beginning of the list and clamping is not enabled then
   * the call will "wrap-around" and return the last character in the list.
   */
  char Pred(void) const;

  /**
   * Returns the successor to the object's current character value.  If the object is at the end
   * of the charatcer list and clamping is enabled it returns the last character in the list. If
   * the object is at the end of the list and clamping is not enabled then the call will
   * "wrap-around" and return the first character in the list.
   */
  char Succ(void) const;

  /**
   * Checks if the specified character is in the objects character list.
   * @param c the character value to check if in the object's character list
   * @return true if the character is in the object's character list and false otherwise
   */
  bool IsMember(char c) const;

 /**
   * Prefix increment operator.  Increments the character value of the OrderedChar object.
   * If the object is at the end of the charatcer list and clamping is enabled the value of
   * the object is unchanged.  If the object is at the end of the list and clamping is not
   * enabled then value will "wrap-around" and object value will be set to the first
   * character in the list.
   * @return the value of the object before it is incremented
   */
  OrderedChar& operator++();

   /**
   * Prefix decrement operator.  Decrements the character value of the OrderedChar object.
   * If the object is at the beginning of the charatcer list and clamping is enabled the value
   * of the object is unchanged.  If the object is at the beginning of the list and clamping
   * is not enabled then value will "wrap-around" and object value will be set to the last
   * character in the list.
   * @return the value of the object before it is decremented
   */
  OrderedChar& operator--();

   /**
   * Postfix increment operator.  Increments the character value of the OrderedChar object.
   * If the object is at the end of the charatcer list and clamping is enabled the value of the
   * object is unchanged.  If the object is at the end of the list and clamping is not enabled
   * then value will "wrap-around" and object value will be set to the first character in the
   * list.
   * @return the value of the object after it is incremented
   */
  OrderedChar operator++(int);

   /**
   * Postfix decrement operator.  Decrements the character value of the OrderedChar object.
   * If the object is at the beginning of the charatcer list and clamping is enabled the value
   * of the object is unchanged.  If the object is at the beginning of the list and clamping
   * is not enabled then value will "wrap-around" and object value will be set to the last
   * character in the list.
   * @return the value of the object after it is incremented
   */
  OrderedChar operator--(int);

 private:
  std::string cs_ = "\0";
  std::size_t index_ = 0;
  bool clamp_ = false;

  bool HasUniqueChars(std::string_view s) const;
  std::size_t FindCharIndex(std::string_view s, char c) const;
};

}  // namespace BurningLogic

#endif

ordered_char.cc

#include "ordered_char.h"

#include <stdexcept>

namespace BurningLogic {

const std::string empty_char_string_text = "char_string must be non-empty";
const std::string invalid_char_string_text = "char_string must entries must be unique";
const std::string char_not_found_text = "inital_char not found in char_string";

OrderedChar::OrderedChar(std::string_view char_string, bool clamp) {
  if (char_string.length() == 0) {
    throw std::invalid_argument(empty_char_string_text);
  }

  if (HasUniqueChars(char_string)) {
    cs_ = char_string;
  } else {
    throw std::invalid_argument(invalid_char_string_text);
  }

  clamp_ = clamp;
}

OrderedChar::OrderedChar(std::string_view char_string, char inital_char, bool clamp) {
  if (char_string.length() == 0) {
    throw std::invalid_argument(empty_char_string_text);
  }

  if (HasUniqueChars(char_string)) {
    cs_ = char_string;
  } else {
    throw std::invalid_argument(invalid_char_string_text);
  }

  if (!IsMember(inital_char)) {
    throw std::invalid_argument(char_not_found_text);
  }

  index_ = FindCharIndex(char_string, inital_char);

  clamp_ = clamp;
}

char OrderedChar::GetChar(void) const {
  return cs_[index_];
}

void OrderedChar::SetChar(char c) {
  index_ = FindCharIndex(cs_, c);
}

char OrderedChar::Pred(void) const {
  std::size_t tmp_index;

  if (index_ != 0) {
    tmp_index = index_ - 1;
  } else {
    if (clamp_) {
      tmp_index = 0;
    } else {
      tmp_index = cs_.length() - 1;
    }
  }

  return cs_[tmp_index];
}

char OrderedChar::Succ(void) const {
  std::size_t tmp_index;

  if (index_ != (cs_.length() - 1)) {
    tmp_index = index_ + 1;
  } else {
    if (clamp_) {
      tmp_index = cs_.length() - 1;
    } else {
      tmp_index = 0;
    }
  }

  return cs_[tmp_index];
}

bool OrderedChar::IsMember(char c) const {
  for (auto& this_char : cs_) {
    if (this_char == c) {
      return true;
    }
  }

  return false;
}

OrderedChar& OrderedChar::operator++() {
  if (index_ != (cs_.length() - 1)) {
    ++index_;
  } else {
    if (!clamp_) {
      index_ = 0;
    }
  }

  return *this;
}

OrderedChar& OrderedChar::operator--() {
  if (index_ != 0) {
    --index_;
  } else {
    if (!clamp_) {
      index_ = cs_.length() - 1;
    }
  }

  return *this;
}

OrderedChar OrderedChar::operator++(int) {
  OrderedChar tmp = *this;
  ++*this;
  return tmp;
}

OrderedChar OrderedChar::operator--(int) {
  OrderedChar tmp = *this;
  --*this;
  return tmp;
}

bool OrderedChar::HasUniqueChars(std::string_view s) const {
  std::size_t len = s.length();

  for (std::size_t ii = 0; ii < len; ++ii) {
    char test_char = s[ii];

    for (std::size_t jj = (ii + 1); jj < len; ++jj) {
      if (s[jj] == test_char) {
        return false;  // Exit early if a duplicate charater is found
      }
    }
  }

  return true;
}

std::size_t OrderedChar::FindCharIndex(std::string_view s, char c) const {
  for (std::size_t ii = 0; ii < s.length(); ++ii) {
    if (s[ii] == c) {
      return ii;
    }
  }

  throw std::invalid_argument(char_not_found_text);
}

}  // namespace BurningLogic

test_main.cc

#include <iostream>
#include <stdexcept>

#include "ordered_char.h"

using namespace BurningLogic;

void Test01(void) {
  // Try OrderedChar(std::string_view char_string) constructor with empty string
  bool caught = false;

  try {
    OrderedChar oc("");
  }

  catch (std::invalid_argument& e) {
    std::cout << "Test01 PASS: Caught '" << e.what() << "' (Expected)\n";
    caught = true;
  }

  if (!caught) {
    std::cout << "Test01 FAIL\n";
  }
}

void Test02(void) {
  // Try OrderedChar(std::string_view char_string) constructor with repeating chars
  bool caught = false;

  try {
    OrderedChar oc("lcnwnciwuenuuj");
  }

  catch (std::invalid_argument& e) {
    std::cout << "Test02 PASS: Caught '" << e.what() << "' (Expected)\n";
    caught = true;
  }

  if (!caught) {
    std::cout << "Test01 FAIL\n";
  }
}

void Test03(void) {
  // Try various valid constructor combinations
  bool caught = false;

  try {
    OrderedChar oc1("abc123zyx");         // OrderedChar(char_string)
    OrderedChar oc2("zxc098vbn", '8');    // OrderedChar(char_string, inital_char)
    OrderedChar oc3("asdfghjkl", true);   // OrderedChar(char_string, clamp)
    OrderedChar oc4("zxcvbnm12", false);  // OrderedChar(char_string, clamp)
    OrderedChar oc5("qwertyuio", 'r',
                    true);  // OrderedChar(char_string, inital_char, clamp)
    OrderedChar oc6("1qaz2wsx3", 'q',
                    false);  // OrderedChar(char_string, inital_char, clamp)
  }

  catch (std::invalid_argument& e) {
    caught = true;
  }

  if (caught) {
    std::cout << "Test03 FAIL: One or more exceptions thrown\n";
  } else {
    std::cout << "Test03 PASS\n";
  }
}

void Test04(void) {
  // Test GetChar, SetChar, Pred, Succ, and IsMember
  bool caught = false;

  try {
    OrderedChar oc_no_clamp("abcdefghijklm", 'e');
    OrderedChar oc_clamp("abcdefghijklm", 'g', true);

    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'e'\n";
    std::cout << "oc_no_clamp.IsMember('f') = " << oc_no_clamp.IsMember('f') << " expected 1\n";
    std::cout << "oc_no_clamp.IsMember('7') = " << oc_no_clamp.IsMember('7') << " expected 0\n";

    std::cout << "oc_no_clamp.SetChar('a')\n";
    oc_no_clamp.SetChar('a');
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'a'\n";
    std::cout << "oc_no_clamp.Pred() = '" << oc_no_clamp.Pred() << "' expected 'm'\n";
    std::cout << "oc_no_clamp.Succ() = '" << oc_no_clamp.Succ() << "' expected 'b'\n";

    std::cout << "oc_no_clamp.SetChar('m')\n";
    oc_no_clamp.SetChar('m');
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'm'\n";
    std::cout << "oc_no_clamp.Pred() = '" << oc_no_clamp.Pred() << "' expected 'l'\n";
    std::cout << "oc_no_clamp.Succ() = '" << oc_no_clamp.Succ() << "' expected 'a'\n";

    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'g'\n";
    std::cout << "oc_clamp.IsMember('b') = " << oc_clamp.IsMember('b') << " expected 1\n";
    std::cout << "oc_clamp.IsMember('x') = " << oc_clamp.IsMember('x') << " expected 0\n";

    std::cout << "oc_clamp.SetChar('a')\n";
    oc_clamp.SetChar('a');
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'a'\n";
    std::cout << "oc_clamp.Pred() = '" << oc_clamp.Pred() << "' expected 'a'\n";
    std::cout << "oc_clamp.Succ() = '" << oc_clamp.Succ() << "' expected 'b'\n";

    std::cout << "oc_clamp.SetChar('m')\n";
    oc_clamp.SetChar('m');
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'm'\n";
    std::cout << "oc_clamp.Pred() = '" << oc_clamp.Pred() << "' expected 'l'\n";
    std::cout << "oc_clamp.Succ() = '" << oc_clamp.Succ() << "' expected 'm'\n";
  }

  catch (std::invalid_argument& e) {
    caught = true;
  }

  if (caught) {
    std::cout << "Test04 FAIL: One or more exceptions thrown\n";
  } else {
    std::cout << "Test04 No exceptions thrown\n";
  }
}

void Test05(void) {
  // Test pre/post increment/decrement for no clamp
  bool caught = false;

  try {
    OrderedChar oc_no_clamp("abcdefghijklm");

    // No clamp in the middle, decrement
    std::cout << "oc_no_clamp.SetChar('e')\n";
    oc_no_clamp.SetChar('e');
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'e'\n";
    std::cout << "oc_no_clamp--.GetChar() = '" << oc_no_clamp--.GetChar() << "' expected 'e'\n";
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'd'\n";
    std::cout << "(--oc_no_clamp).GetChar() = '" << (--oc_no_clamp).GetChar() << "' expected 'c'\n";
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'c'\n";

    // No clamp in the middle, increment
    std::cout << "oc_no_clamp.SetChar('e')\n";
    oc_no_clamp.SetChar('e');
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'e'\n";
    std::cout << "oc_no_clamp++.GetChar() = '" << oc_no_clamp++.GetChar() << "' expected 'e'\n";
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'f'\n";
    std::cout << "(++oc_no_clamp).GetChar() = '" << (++oc_no_clamp).GetChar() << "' expected 'g'\n";
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'g'\n";

    // No clamp at the beginning, decrement
    std::cout << "oc_no_clamp.SetChar('a')\n";
    oc_no_clamp.SetChar('a');
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'a'\n";
    std::cout << "oc_no_clamp--.GetChar() = '" << oc_no_clamp--.GetChar() << "' expected 'a'\n";
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'm'\n";
    std::cout << "oc_no_clamp.SetChar('a')\n";
    oc_no_clamp.SetChar('a');
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'a'\n";
    std::cout << "(--oc_no_clamp).GetChar() = '" << (--oc_no_clamp).GetChar() << "' expected 'm'\n";
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'm'\n";

    // No clamp at the end, increment
    std::cout << "oc_no_clamp.SetChar('m')\n";
    oc_no_clamp.SetChar('m');
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'm'\n";
    std::cout << "oc_no_clamp++.GetChar() = '" << oc_no_clamp++.GetChar() << "' expected 'm'\n";
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'a'\n";
    std::cout << "oc_no_clamp.SetChar('m')\n";
    oc_no_clamp.SetChar('m');
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'm'\n";
    std::cout << "(++oc_no_clamp).GetChar() = '" << (++oc_no_clamp).GetChar() << "' expected 'a'\n";
    std::cout << "oc_no_clamp.GetChar() = '" << oc_no_clamp.GetChar() << "' expected 'a'\n";
  }

  catch (std::invalid_argument& e) {
    caught = true;
  }

  if (caught) {
    std::cout << "Test05 FAIL: One or more exceptions thrown\n";
  } else {
    std::cout << "Test05 No exceptions thrown\n";
  }
}

void Test06(void) {
  // Test pre/post increment/decrement for with clamping
  bool caught = false;

  try {
    OrderedChar oc_clamp("abcdefghijklm", true);

    // Clamp in the middle, decrement
    std::cout << "oc_clamp.SetChar('e')\n";
    oc_clamp.SetChar('e');
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'e'\n";
    std::cout << "oc_clamp--.GetChar() = '" << oc_clamp--.GetChar() << "' expected 'e'\n";
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'd'\n";
    std::cout << "(--oc_clamp).GetChar() = '" << (--oc_clamp).GetChar() << "' expected 'c'\n";
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'c'\n";

    // Clamp in the middle, increment
    std::cout << "oc_clamp.SetChar('e')\n";
    oc_clamp.SetChar('e');
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'e'\n";
    std::cout << "oc_clamp++.GetChar() = '" << oc_clamp++.GetChar() << "' expected 'e'\n";
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'f'\n";
    std::cout << "(++oc_clamp).GetChar() = '" << (++oc_clamp).GetChar() << "' expected 'g'\n";
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'g'\n";

    // Clamp at the beginning, decrement
    std::cout << "oc_clamp.SetChar('a')\n";
    oc_clamp.SetChar('a');
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'a'\n";
    std::cout << "oc_clamp--.GetChar() = '" << oc_clamp--.GetChar() << "' expected 'a'\n";
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'a'\n";
    std::cout << "oc_clamp.SetChar('a')\n";
    oc_clamp.SetChar('a');
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'a'\n";
    std::cout << "(--oc_clamp).GetChar() = '" << (--oc_clamp).GetChar() << "' expected 'a'\n";
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'a'\n";

    // Clamp at the end, increment
    std::cout << "oc_clamp.SetChar('m')\n";
    oc_clamp.SetChar('m');
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'm'\n";
    std::cout << "oc_clamp++.GetChar() = '" << oc_clamp++.GetChar() << "' expected 'm'\n";
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'm'\n";
    std::cout << "oc_clamp.SetChar('m')\n";
    oc_clamp.SetChar('m');
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'm'\n";
    std::cout << "(++oc_clamp).GetChar() = '" << (++oc_clamp).GetChar() << "' expected 'm'\n";
    std::cout << "oc_clamp.GetChar() = '" << oc_clamp.GetChar() << "' expected 'm'\n";
  }

  catch (std::invalid_argument& e) {
    caught = true;
  }

  if (caught) {
    std::cout << "Test06 FAIL: One or more exceptions thrown\n";
  } else {
    std::cout << "Test06 No exceptions thrown\n";
  }
}

int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
  Test01();
  Test02();
  Test03();
  Test04();
  Test05();
  Test06();
}