Exercise: Singleton (Flexible And Strict)

Complications

In Exercise: Singleton (Flexible), we showed a more flexible way to instantiate the instance at runtime.

Unfortunately, that implementation has the its own shortcomings:

  • Overwriting the instance can only result in a runtime error

  • Program startup has to be crafted carefully

  • Users are now explicitly enabled to instantiate either of the two implementation (alas, it’s the user who writes startup code)

  • All in all: users see that there are concrete implementations, and must be told rules:

    • Only instantiate concrete implementation at startup

    • After startup, only access interface via SocialInsurance::instance()

Quick fix: re-instantiate the requirements from Exercise: Singleton (Inflexible):

  • Eliminate the offending SocialInsurance::set_instance() method.

  • Introduce an environment variable SOCINSUR that is set to either name() of the available implementations (i.e., SVS or OEGK).

  • Modify SocialInsurance::instance() to read the SOCINSUR environment variable, and to instantiate the respective inisurance implementation.

Reimplement the class hierarchy such that the following program can run. Take special care:

  • Uncomment the commented-out lines and make sure that they emit compiler errors.

  • There must not be a memory leak at program end.

#include "social-insurance-flexible-strict.h"

// only there to deny compiability below
// #include "social-insurance-svs-strict.h"
// #include "social-insurance-oegk-strict.h"

#include <iostream>
#include <string>


int main()
{
    std::string id("1037190666");

    SocialInsurance::instance().charge(id, 1254.60);
    SocialInsurance::instance().charge(id, 231.34);

    std::cout << id << " owes \"" << SocialInsurance::instance().name() << "\" " << SocialInsurance::instance().debt(id) << " Euros" << std::endl;

    // MUST NOT COMPILE
    // ================

    // explicit instantiation (neither iface nor implementations)
    // ----------------------------------------------------------

    // SocialInsurance iface_instance("Another Insurance");
    // SVS svs_instance;
    // OEGK oegk_instance;

    // copy initialization
    // -------------------

    // SocialInsurance another_instance = SocialInsurance::instance();

    // copy assignment
    // ---------------

    // another_instance = SocialInsurance::instance();

    // and dynamic_cast?
    // -----------------

    // STOP HERE!!!

    return 0;
}

Tests

  • SOCINSUR=OEGK

    $ SOCINSUR=OEGK ./singleton-social-insurance-flexible-strict-main
    1037190666 owes "OEGK" 1485.94 Euros
    
  • SOCINSUR=SVS

    $ SOCINSUR=SVS ./singleton-social-insurance-flexible-strict-main
    1037190666 owes "SVS" 1634.53 Euros
    
  • SOCINSUR unset

    $ ./singleton-social-insurance-flexible-strict-main
    terminate called after throwing an instance of 'std::runtime_error'
      what():  Environment variable "SOCINSUR" is not set
    Aborted (core dumped)
    
  • SOCINSUR invalid

    $ SOCINSUR=xxx ./singleton-social-insurance-flexible-strict-main
    terminate called after throwing an instance of 'std::runtime_error'
      what():  Environment variable "SOCINSUR" has invalid value
    Aborted (core dumped)