- Add unified Singleton support
git-svn-id: svn://svn.gna.org/svn/sgpemv2/trunk@643 3ecf2c5c-341e-0410-92b4-d18e462d057c
This commit is contained in:
parent
8ca3a61730
commit
383889a203
16
Makefile.am
16
Makefile.am
|
@ -48,6 +48,7 @@ bin_PROGRAMS =
|
|||
plugin_LTLIBRARIES =
|
||||
noinst_HEADERS =
|
||||
pkglib_LTLIBRARIES =
|
||||
pkginclude_HEADERS =
|
||||
EXTRA_DIST =
|
||||
MAINTAINERCLEANFILES =
|
||||
MOSTLYCLEANFILES =
|
||||
|
@ -127,6 +128,7 @@ pkglib_LTLIBRARIES += src/backend/libbackend.la
|
|||
|
||||
src_backend_libbackend_la_CPPFLAGS = \
|
||||
-I@top_srcdir@ \
|
||||
-I@top_srcdir@/src/templates \
|
||||
-DPOLDIR="\"$(policiesdir)\"" \
|
||||
-DPLUGDIR="\"$(plugindir)\"" \
|
||||
-DLOCALEDIR="\"$(localedir)\"" \
|
||||
|
@ -163,7 +165,7 @@ src_backend_libbackend_la_SOURCES = \
|
|||
src/backend/thread.cc \
|
||||
src/backend/user_interrupt_exception.cc
|
||||
|
||||
pkginclude_HEADERS = \
|
||||
pkginclude_HEADERS += \
|
||||
config.h \
|
||||
src/backend/dynamic_process.hh \
|
||||
src/backend/dynamic_schedulable.hh \
|
||||
|
@ -198,6 +200,7 @@ bin_PROGRAMS += sgpemv2
|
|||
|
||||
sgpemv2_CPPFLAGS = \
|
||||
-I@top_srcdir@ \
|
||||
-I@top_srcdir@/src/templates \
|
||||
-DLOCALEDIR="\"$(localedir)\"" \
|
||||
$(CAIRO_CFLAGS) \
|
||||
$(GTKMM_CFLAGS) \
|
||||
|
@ -242,10 +245,12 @@ noinst_HEADERS += \
|
|||
#
|
||||
# ############################################################
|
||||
|
||||
noinst_HEADERS += \
|
||||
pkginclude_HEADERS += \
|
||||
src/templates/parameter.tcc \
|
||||
src/templates/smartp.tcc \
|
||||
src/templates/smartp.hh
|
||||
src/templates/singleton.hh \
|
||||
src/templates/singleton.tcc \
|
||||
src/templates/smartp.hh \
|
||||
src/templates/smartp.tcc
|
||||
|
||||
# ############################################################
|
||||
#
|
||||
|
@ -266,6 +271,7 @@ noinst_PROGRAMS = \
|
|||
|
||||
src_testsuite_test_history_CPPFLAGS = \
|
||||
-I@top_srcdir@/src \
|
||||
-I@top_srcdir@/src/templates \
|
||||
$(GLIBMM_CFLAGS)
|
||||
src_testsuite_test_history_LDFLAGS = \
|
||||
src/backend/libbackend.la \
|
||||
|
@ -277,6 +283,7 @@ src_testsuite_test_history_SOURCES = \
|
|||
|
||||
#src_testsuite_test_parse_command_CPPFLAGS = \
|
||||
# -I@top_srcdir@/src \
|
||||
# -I@top_srcdir@/src/templates \
|
||||
# $(GLIBMM_CFLAGS)
|
||||
#src_testsuite_test_parse_command_LDFLAGS = \
|
||||
# src/backend/libbackend.la \
|
||||
|
@ -288,6 +295,7 @@ src_testsuite_test_history_SOURCES = \
|
|||
# advice: get dummy_policy from the somewhere in the repository, and compile it in.
|
||||
#src_testsuite_test_stepforward_CPPFLAGS = \
|
||||
# -I@top_srcdir@/src \
|
||||
# -I@top_srcdir@/src/templates \
|
||||
# $(GLIBMM_CFLAGS)
|
||||
#src_testsuite_test_stepforward_LDFLAGS = \
|
||||
# src/backend/libbackend.la \
|
||||
|
|
|
@ -14,4 +14,4 @@ URL: http://www.math.unipd.it/
|
|||
Requires: glibmm-2.4 >= 2.8 gthread-2.0 >= 2.8
|
||||
Libs: -L${libdir}/src/backend -lbackend
|
||||
Libs.private: -lglibmm-2.4 -lgthread-2.0
|
||||
Cflags: -I${includedir}/backend
|
||||
Cflags: -I${includedir}/backend -I${includedir}/templates
|
||||
|
|
|
@ -107,10 +107,11 @@ PythonPolicyManager::init()
|
|||
// environment variables.
|
||||
// FIXME: find better way to achieve this.
|
||||
|
||||
GlobalPreferences& prefs = GlobalPreferences::get_instance();
|
||||
Glib::ustring importdirs = "import sys\n"
|
||||
"sys.path[:0] = [ ";
|
||||
for_each(GlobalPreferences::instance().policies_dir_begin(),
|
||||
GlobalPreferences::instance().policies_dir_end(),
|
||||
for_each(prefs.policies_dir_begin(),
|
||||
prefs.policies_dir_end(),
|
||||
pol_dirs_concat(importdirs));
|
||||
importdirs += " '" SHAREDIR "' ]\n";
|
||||
|
||||
|
@ -131,8 +132,9 @@ PythonPolicyManager::get_avail_policies()
|
|||
void
|
||||
PythonPolicyManager::collect_policies()
|
||||
{
|
||||
GlobalPreferences::dir_iterator dir_it = GlobalPreferences::instance().policies_dir_begin();
|
||||
GlobalPreferences::dir_iterator dir_end = GlobalPreferences::instance().policies_dir_end();
|
||||
GlobalPreferences& prefs = GlobalPreferences::get_instance();
|
||||
GlobalPreferences::dir_iterator dir_it = prefs.policies_dir_begin();
|
||||
GlobalPreferences::dir_iterator dir_end = prefs.policies_dir_end();
|
||||
|
||||
for(; dir_it != dir_end; ++dir_it)
|
||||
{
|
||||
|
|
|
@ -67,7 +67,7 @@ main(int argc, char** argv) {
|
|||
}
|
||||
else
|
||||
// Add argv[1] as the directory to search for uninstalled policies
|
||||
sgpem::GlobalPreferences::instance().add_policies_dir(argv[1]);
|
||||
sgpem::GlobalPreferences::get_instance().add_policies_dir(argv[1]);
|
||||
|
||||
// Self-register itself to Scheduler, however we don't care about it
|
||||
TestPythonPolicyManager polman;
|
||||
|
|
|
@ -23,17 +23,6 @@
|
|||
|
||||
using namespace sgpem;
|
||||
|
||||
GlobalPreferences* GlobalPreferences::_instance = 0;
|
||||
|
||||
GlobalPreferences&
|
||||
GlobalPreferences::instance()
|
||||
{
|
||||
if(!_instance)
|
||||
_instance = new GlobalPreferences();
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
|
||||
GlobalPreferences::GlobalPreferences()
|
||||
: _mod_dirs(1, PLUGDIR), _pol_dirs(1, POLDIR)
|
||||
{}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <glibmm/ustring.h>
|
||||
#include <vector>
|
||||
|
||||
#include "singleton.hh"
|
||||
|
||||
namespace sgpem {
|
||||
class GlobalPreferences;
|
||||
}
|
||||
|
@ -33,11 +35,12 @@ namespace sgpem {
|
|||
#include "config.h"
|
||||
|
||||
namespace sgpem {
|
||||
class SG_DLLEXPORT GlobalPreferences {
|
||||
public:
|
||||
typedef std::vector<Glib::ustring>::const_iterator dir_iterator;
|
||||
class SG_DLLEXPORT GlobalPreferences : public Singleton<GlobalPreferences>
|
||||
{
|
||||
friend class Singleton<GlobalPreferences>;
|
||||
|
||||
static GlobalPreferences& instance();
|
||||
public:
|
||||
typedef std::vector<Glib::ustring>::const_iterator dir_iterator;
|
||||
|
||||
dir_iterator modules_dir_begin() const;
|
||||
dir_iterator modules_dir_end() const;
|
||||
|
@ -53,7 +56,6 @@ namespace sgpem {
|
|||
GlobalPreferences(const GlobalPreferences&);
|
||||
GlobalPreferences& operator=(const GlobalPreferences&);
|
||||
|
||||
static GlobalPreferences* _instance;
|
||||
std::vector<Glib::ustring> _mod_dirs;
|
||||
std::vector<Glib::ustring> _pol_dirs;
|
||||
};
|
||||
|
|
|
@ -23,27 +23,15 @@ using namespace std;
|
|||
using namespace sgpem;
|
||||
using namespace memory;
|
||||
|
||||
//History::instance; //static object
|
||||
History* History::_instance = 0;
|
||||
|
||||
/**
|
||||
The constructor sets _total_time_elapsed to -1: this permits to insert the INITIAL STATUS
|
||||
of the simulation which must begin at instant -1 and live for 1 instant.
|
||||
*/
|
||||
History::History() //private constructor. The parameter is discarded
|
||||
History::History() //private constructor.
|
||||
:_total_time_elapsed(-1)
|
||||
{}
|
||||
|
||||
|
||||
History&
|
||||
History::get_instance()
|
||||
{
|
||||
if(!_instance)
|
||||
_instance = new History();
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Returns a pointer to a copy of the DynamicSchedulable object relative to this instant.
|
||||
It can be NULL if time is out of range or if there are no running entities in the associated
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "dynamic_schedulable.hh"
|
||||
#include "../templates/smartp.hh"
|
||||
|
||||
#include "singleton.hh"
|
||||
|
||||
namespace sgpem
|
||||
{
|
||||
|
||||
|
@ -47,8 +49,10 @@ namespace sgpem
|
|||
*/
|
||||
class History;
|
||||
|
||||
class SG_DLLEXPORT History : public ObservedSubject
|
||||
class SG_DLLEXPORT History : public Singleton<History>, public ObservedSubject
|
||||
{
|
||||
friend class Singleton<History>;
|
||||
|
||||
public:
|
||||
/**
|
||||
Gets the \ref Schedulable object running at the specified time.
|
||||
|
@ -83,15 +87,8 @@ namespace sgpem
|
|||
virtual void truncate_at(int instant);
|
||||
|
||||
|
||||
/**
|
||||
Gets the only instance of History.
|
||||
\return The Singleton instance of this object.
|
||||
*/
|
||||
static History& get_instance();
|
||||
|
||||
protected:
|
||||
History(); //private constructor. The parameter is discarded
|
||||
static History* _instance;
|
||||
History(); //private constructor.
|
||||
|
||||
private:
|
||||
int _total_time_elapsed;
|
||||
|
|
|
@ -34,16 +34,6 @@ using namespace sgpem;
|
|||
typedef vector<PolicyManager*>::iterator ManagerIterator;
|
||||
typedef map<History*, Policy*>::iterator ActiveIterator;
|
||||
|
||||
PoliciesGatekeeper* PoliciesGatekeeper::_instance = NULL;
|
||||
|
||||
PoliciesGatekeeper&
|
||||
PoliciesGatekeeper::get_instance()
|
||||
{
|
||||
if(!_instance)
|
||||
_instance = new PoliciesGatekeeper();
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
vector<PolicyManager*>
|
||||
PoliciesGatekeeper::get_registered() const
|
||||
{
|
||||
|
|
|
@ -34,6 +34,8 @@ namespace sgpem
|
|||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "singleton.hh"
|
||||
|
||||
namespace sgpem
|
||||
{
|
||||
class PoliciesGatekeeper;
|
||||
|
@ -43,15 +45,11 @@ namespace sgpem
|
|||
|
||||
*/
|
||||
|
||||
class SG_DLLEXPORT PoliciesGatekeeper
|
||||
class SG_DLLEXPORT PoliciesGatekeeper : public Singleton<PoliciesGatekeeper>
|
||||
{
|
||||
public:
|
||||
/** \brief Returns the unique instance of this class, conforming to the Singleton pattern.
|
||||
*
|
||||
* \return the unique instance of this class, conforming to the Singleton pattern.
|
||||
*/
|
||||
static PoliciesGatekeeper& get_instance();
|
||||
friend class Singleton<PoliciesGatekeeper>;
|
||||
|
||||
public:
|
||||
std::vector<PolicyManager*> get_registered() const;
|
||||
|
||||
void register_manager(PolicyManager* manager);
|
||||
|
@ -68,7 +66,6 @@ namespace sgpem
|
|||
// Deactivates active policies managed by the specified manager.
|
||||
void deactivate_policies(PolicyManager* manager);
|
||||
|
||||
static PoliciesGatekeeper* _instance;
|
||||
std::vector<PolicyManager*> _registered;
|
||||
std::map<History*, Policy*> _active_policies;
|
||||
};
|
||||
|
|
|
@ -29,9 +29,6 @@ using namespace std;
|
|||
using namespace sgpem;
|
||||
using namespace memory;
|
||||
|
||||
Scheduler*
|
||||
Scheduler::_instance = 0;
|
||||
|
||||
//private constructor. The parameter is discarded
|
||||
Scheduler::Scheduler()
|
||||
: _policy_manager(PolicyManager::get_registered_manager())
|
||||
|
@ -39,14 +36,6 @@ Scheduler::Scheduler()
|
|||
_policy_manager.init();
|
||||
}
|
||||
|
||||
Scheduler&
|
||||
Scheduler::get_instance()
|
||||
{
|
||||
if(!_instance)
|
||||
_instance = new Scheduler();
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
SchedulableQueue*
|
||||
Scheduler::get_ready_queue()
|
||||
{
|
||||
|
|
|
@ -37,11 +37,13 @@ namespace sgpem
|
|||
#include "schedulable_queue.hh"
|
||||
#include "user_interrupt_exception.hh"
|
||||
|
||||
#include "singleton.hh"
|
||||
|
||||
namespace sgpem
|
||||
{
|
||||
class Scheduler;
|
||||
class Scheduler;
|
||||
|
||||
/** \brief Manages the DynamicSchedulable objects, implementing a given policy.
|
||||
/** \brief Manages the DynamicSchedulable objects, implementing a given policy.
|
||||
|
||||
Class Scheduler manages the schedulable entities which are ready to run,
|
||||
ordering them in a queue; it also checks that the current scheduling policy
|
||||
|
@ -50,61 +52,49 @@ namespace sgpem
|
|||
the DynamicSchedulable objects (for further details about this, check
|
||||
class DynamicSchedulable).
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
class SG_DLLEXPORT Scheduler
|
||||
{
|
||||
public:
|
||||
/** \brief Returns the unique instance of this class, conforming to the Singleton pattern.
|
||||
*
|
||||
* If Scheduler isn't initialized, creates it. Should be called at least once before
|
||||
* starting the Simulation.
|
||||
* \return the unique instance of this class, conforming to the Singleton pattern.
|
||||
*/
|
||||
static Scheduler& get_instance();
|
||||
/**
|
||||
Returns a pointer to the queue containing all the ready
|
||||
schedulable objects (for the policy to sort it).
|
||||
\return a pointer to the queue containing all the ready
|
||||
schedulable objects (for the policy to sort it).
|
||||
*/
|
||||
SchedulableQueue* get_ready_queue();
|
||||
/**
|
||||
Resets the simulation to the initial state.
|
||||
*/
|
||||
void reset_status();
|
||||
/**
|
||||
Generates a new SchedulableQueue representing the status of the processes
|
||||
at the simulation instant next to the current one, and extends the History by
|
||||
one instant with it.
|
||||
*/
|
||||
void step_forward() throw(UserInterruptException);
|
||||
/**
|
||||
Sets the policy that will be used to generate the simulation at the next instant.
|
||||
\param policy the policy that will be used to generate the simulation at the next instant.
|
||||
*/
|
||||
/* DISABLED until we don't have PolicyManager::set_policy()
|
||||
void set_policy(Policy* policy);
|
||||
*/
|
||||
/**
|
||||
Returns the policy that will be used to generate the simulation at the next instant.
|
||||
\return the policy that will be used to generate the simulation at the next instant.
|
||||
*/
|
||||
Policy& get_policy();
|
||||
class SG_DLLEXPORT Scheduler : public Singleton<Scheduler>
|
||||
{
|
||||
friend class Singleton<Scheduler>;
|
||||
public:
|
||||
/**
|
||||
Returns a pointer to the queue containing all the ready
|
||||
schedulable objects (for the policy to sort it).
|
||||
\return a pointer to the queue containing all the ready
|
||||
schedulable objects (for the policy to sort it).
|
||||
*/
|
||||
SchedulableQueue* get_ready_queue();
|
||||
/**
|
||||
Resets the simulation to the initial state.
|
||||
*/
|
||||
void reset_status();
|
||||
/**
|
||||
Generates a new SchedulableQueue representing the status of the processes
|
||||
at the simulation instant next to the current one, and extends the History by
|
||||
one instant with it.
|
||||
*/
|
||||
void step_forward() throw(UserInterruptException);
|
||||
/**
|
||||
Sets the policy that will be used to generate the simulation at the next instant.
|
||||
\param policy the policy that will be used to generate the simulation at the next instant.
|
||||
*/
|
||||
/* DISABLED until we don't have PolicyManager::set_policy()
|
||||
void set_policy(Policy* policy);
|
||||
*/
|
||||
/**
|
||||
Returns the policy that will be used to generate the simulation at the next instant.
|
||||
\return the policy that will be used to generate the simulation at the next instant.
|
||||
*/
|
||||
Policy& get_policy();
|
||||
|
||||
|
||||
private:
|
||||
Scheduler(); //private constructor.
|
||||
static Scheduler* _instance;
|
||||
SchedulableQueue _ready_queue;
|
||||
PolicyManager& _policy_manager;
|
||||
};
|
||||
private:
|
||||
Scheduler(); //private constructor.
|
||||
SchedulableQueue _ready_queue;
|
||||
PolicyManager& _policy_manager;
|
||||
};
|
||||
|
||||
}//~ namespace sgpem
|
||||
|
||||
#endif //SCHEDULER_HH
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -63,8 +63,9 @@ static void load_pyloader_plugin() {
|
|||
// Leaks willingly:
|
||||
Module* pyloader = 0;
|
||||
|
||||
GlobalPreferences::dir_iterator it = GlobalPreferences::instance().modules_dir_begin();
|
||||
while(it != GlobalPreferences::instance().modules_dir_end()) {
|
||||
GlobalPreferences& prefs = GlobalPreferences::get_instance();
|
||||
GlobalPreferences::dir_iterator it = prefs.modules_dir_begin();
|
||||
while(it != prefs.modules_dir_end()) {
|
||||
std::string pyloader_path = Module::build_path(*it, "pyloader");
|
||||
pyloader = new Module(pyloader_path);
|
||||
if(*pyloader) break;
|
||||
|
|
|
@ -80,10 +80,10 @@ parse_options(int& argc, char**& argv)
|
|||
// FIXME : to be written!
|
||||
break;
|
||||
case 'P':
|
||||
GlobalPreferences::instance().add_policies_dir(optarg);
|
||||
GlobalPreferences::get_instance().add_policies_dir(optarg);
|
||||
break;
|
||||
case 'M':
|
||||
GlobalPreferences::instance().add_modules_dir(optarg);
|
||||
GlobalPreferences::get_instance().add_modules_dir(optarg);
|
||||
break;
|
||||
case ':':
|
||||
printf(_("[EE] Wrong number of parameters. Please see \n"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Src/templates/parameter.tcc - Copyright 2005, 2006, University
|
||||
// src/templates/parameter.tcc - Copyright 2005, 2006, University
|
||||
// of Padova, dept. of Pure and Applied
|
||||
// Mathematics
|
||||
//
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// singleton.hh - Copyright 2005, 2006, University
|
||||
// of Padova, dept. of Pure and Applied
|
||||
// Mathematics
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef SINGLETON_HH
|
||||
#define SINGLETON_HH 1
|
||||
|
||||
#include <glibmm/thread.h>
|
||||
#include "config.h"
|
||||
|
||||
namespace sgpem
|
||||
{
|
||||
|
||||
/** \brief An abstract implementation of the Singleton design pattern.
|
||||
*
|
||||
* Singleton implementers constuctor will have to declare friendliness
|
||||
* to Singleton::get_instance(). This also attempts to achieve
|
||||
* thread-safeness.
|
||||
*/
|
||||
template<typename Instantiated_class>
|
||||
class SG_DLLEXPORT Singleton
|
||||
{
|
||||
public:
|
||||
|
||||
/** \brief Ensures thread safety is respected, and returns the instantiated object
|
||||
*
|
||||
* This is done by locking _instance via a mutex, before intantiating the
|
||||
* Instantiated_class attribute (if not done before).
|
||||
*
|
||||
* \return The instantiated object
|
||||
*/
|
||||
static Instantiated_class& get_instance();
|
||||
|
||||
private:
|
||||
static Instantiated_class* _instance;
|
||||
static Glib::StaticMutex _mutex;
|
||||
}; //~ class Singleton
|
||||
|
||||
} //~ namespace sgpem
|
||||
|
||||
|
||||
#include "singleton.tcc"
|
||||
|
||||
#endif //~ SINGLETON_HH
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// singleton.tcc - Copyright 2005, 2006, University
|
||||
// of Padova, dept. of Pure and Applied
|
||||
// Mathematics
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef SINGLETON_TCC
|
||||
#define SINGLETON_TCC 1
|
||||
|
||||
#include "singleton.hh"
|
||||
|
||||
template<typename Instantiated_class>
|
||||
Instantiated_class*
|
||||
sgpem::Singleton<Instantiated_class>::_instance = NULL;
|
||||
|
||||
template<typename Instantiated_class>
|
||||
Glib::StaticMutex
|
||||
sgpem::Singleton<Instantiated_class>::_mutex = GLIBMM_STATIC_MUTEX_INIT;
|
||||
|
||||
|
||||
template<typename Instantiated_class>
|
||||
Instantiated_class&
|
||||
sgpem::Singleton<Instantiated_class>::get_instance()
|
||||
{
|
||||
Glib::Mutex::Lock lock(_mutex);
|
||||
if(_instance == NULL)
|
||||
_instance = new Instantiated_class();
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
|
||||
#endif //~ SINGLETON_TCC
|
||||
|
Loading…
Reference in New Issue