-2

I have enum classes that represent an accounting transaction type e.g.

enum class Liability
{
    mortgage, creditors, overdraft, short_term_loans
};

enum class Income
{
    sales, interest, rent, bad_debts_recovered, surplus, sundry
};

I have another one for expense, asset etc.

I need to make a class that represents a single transaction. Accounting principles in mind, a single transaction consists of two transaction types e.g. an asset and a liability, or an asset and an expense.

How can I design my Transaction class so that it only needs to store two of variables representing these types above, but can be used for any of my designated enum classes? Or is there a better way to design this scenario?

Edit: I have figured out that I might be able to template the Transaction class having the two enums as the different types. My question is - how could I statically assert that the template type is of one of my enum class types?

Gary Allen
  • 1,218
  • 1
  • 13
  • 28
  • Ehh, why not simply something along the lines of `class Transaction { Liability m_l; Income m_i; /* other stuff */ }`? – Jesper Juhl Jul 20 '20 at 19:59
  • An `int` can be used to contain any enum type, but you lose some type safety that way. – Mark Ransom Jul 20 '20 at 20:01
  • @JesperJuhl I could do that. I was hoping there was a better solution. With that solution, the two types cannot be passd in the constructor. Instead I would have to have a method to set the type for each different type – Gary Allen Jul 20 '20 at 20:01
  • @MarkRansom exactly... I would like to keep everything type safe. Also - I would then have to identify the type using a string of some sort, which is far from ideal – Gary Allen Jul 20 '20 at 20:02
  • Define the types of valid combinations as pairs, use a `std::variant` of such pairs. – molbdnilo Jul 20 '20 at 20:02
  • @molbdnilo Really redundant in my opinion. I have four types (which may grow), and since all combinations are valid, I have 6 pairs to cater for. Adding 1 more type means 10 variants. Not really scalable as well. I'm good... :) – Gary Allen Jul 20 '20 at 20:04
  • @Gary Of course they can be passed in a constructor: `class Transaction { Liability m_l; Income m_i; /* other stuff */ public: Transaction (const Liability l, const Income i) : m_l(l), m_i(i) {} };` – Jesper Juhl Jul 20 '20 at 20:05
  • @JesperJuhl The same applies that I would have to have a different constructor for each valid combination. 10 constructors all doing pretty much the same thing doesn't sound fun to me... :( – Gary Allen Jul 20 '20 at 20:05
  • @Gary "delegating constructors" may alleviate some of the pain. – Jesper Juhl Jul 20 '20 at 20:07
  • And why would you need 10 constructors when you only have *two* enums? – Jesper Juhl Jul 20 '20 at 20:13
  • @JesperJuhl I don't - please read my question, right below my code. "I have another one for expense, asset etc." – Gary Allen Jul 20 '20 at 20:15
  • "how could I statically assert that the template type is of one of my enum class types" - `static_assert(std::is_same::value);` or something like that.. see https://en.cppreference.com/w/cpp/types#Type_traits_.28since_C.2B.2B11.29 – Jesper Juhl Jul 20 '20 at 20:19

1 Answers1

0

I ended up using templates:

#include <type_traits>

template<typename Debit, typename Credit>
class Transaction
{
public:
    Transaction(int amount, Debit debitType, Credit creditType)
    {
        constexpr bool validDebitType = (std::is_same<Debit, Expense>::value) || (std::is_same<Debit, Asset>::value) || (std::is_same<Debit, Liability>::value) || (std::is_same<Debit, Income>::value);
        constexpr bool validCreditType = (std::is_same<Credit, Expense>::value) || (std::is_same<Credit, Asset>::value) || (std::is_same<Credit, Liability>::value) || (std::is_same<Credit, Income>::value);
        static_assert(validDebitType && validCreditType);
        m_amount = amount;
        m_debitType = debitType;
        m_creditType = creditType;
    }

private:
    int m_amount;
    Debit m_debitType;
    Credit m_creditType;
};

Still welcome for a better solution

EDIT: Turns out an enum storing the actual type itself was my solution. I created another enum with values Expense, Income, Asset etc. and then created two variables (debit and credit) which are just a union of the above types. Thanks for whoever suggested unions!

Gary Allen
  • 1,218
  • 1
  • 13
  • 28