0

I'd like to choose between two different code paths depending on the value of an enum for a singleton class. Singleton classes inherit from a "CSingleton" type, where T is the type of the new class.

enum class Paths
{
    PATH_ONE = 0,
    PATH_TWO,    
}

template<Paths pathValue>
class Foo : public CSingleton<Foo>
{
public:
    Foo()
    {
        if(pathValue == Paths::PATH_ONE)
             myValue = 11;
        else if(pathValue == Paths::PATH_TWO)
             myValue = 22;
    }

    int myValue;

};

Then when using an object I could do something like this:

assert(11 == Foo<Paths::PATH_ONE>::Instance().myValue);

To be explicit, Instance() is what will create the object.

Does what I'm trying to do have a name? Is there a part of Boost or C++11 that can help me out?

Thanks!

BigHands79
  • 229
  • 1
  • 18
  • 7
    You should probably just ditch the singleton, because they raise more issues than they solve. – Etienne de Martel Jan 15 '14 at 21:31
  • Why are you taking a runtime decision (`if`/`else`) on a compile time value (`pathValue`)? – Borgleader Jan 15 '14 at 21:31
  • 2
    @Borgleader an optimizing compiler might make the difference moot – sehe Jan 15 '14 at 21:32
  • Borgleader: I'm not sure how else to do it. Is there a way to "choose" which ctor to run based off of the enum value? – BigHands79 Jan 15 '14 at 21:33
  • @EtiennedeMartel Improper use of a singleton does that, but he isn't showing how he is using it here ... so that suggestion has no merit. – Zac Howland Jan 15 '14 at 21:34
  • Etienne: I would love to ditch the singleton, but for the problem I'm trying to solve, it's unfortunately the "safest" solution. – BigHands79 Jan 15 '14 at 21:34
  • 1
    @ZacHowland My point is that there is _no_ proper use of a singleton. – Etienne de Martel Jan 15 '14 at 21:35
  • 7
    @ZacHowland: "*When discussing which patterns to drop, we found that we still love them all. (Not really—I'm in favor of dropping Singleton. Its use is almost always a design smell.)*" - Erich Gamma ([source](http://www.informit.com/articles/article.aspx?p=1404056)). – Andy Prowl Jan 15 '14 at 21:36
  • @EtiennedeMartel In which case, your point is entirely inaccurate. That would be no different than saying, "N-Tier'd architectural patterns have no proper use ...." – Zac Howland Jan 15 '14 at 21:37
  • 1
    @BigHands79 [You can](http://coliru.stacked-crooked.com/a/9cbf318434c4f1b5) – Borgleader Jan 15 '14 at 21:38
  • @AndyProwl - If you notice, the *we* in that quote didn't dismiss it; the *he* did. – Zac Howland Jan 15 '14 at 21:38
  • 3
    @ZacHowland: Well, firstly he's not alone, there's a big community of "we" around him; secondly, he's one of the GoF, so if *he* says it's bad, it's likely to be really bad. Finally, although I understand that the "*it's bad don't do it*" advice sounds poor, it is indeed really hard to find a justified usage of singleton and this has been pointed out in many articles, blogs, talks, etc. by many people. See [this excellent blog post](http://jalf.dk/blog/2010/03/singletons-solving-problems-you-didnt-know-you-never-had-since-1995/) for some more information. – Andy Prowl Jan 15 '14 at 21:46
  • @ZacHowland *or* it would be no different than saying "a chocolate kettle has no proper use". If you can think of a "proper use" for a singleton, enlighten us. If you can't, then you are going to have a hard time convincing us that such a thing exists. – jalf Jan 15 '14 at 21:48
  • @AndyProwl You are reading more into what he said. He didn't say it "is bad", he said, "its use is *almost* always a design smell". That isn't saying, "There is never a reason to use it," but rather, "Most people misuse it." And I'm not advocating its use here, but given his question is about templates, stating that he shouldn't be using a design pattern (when he may or may not be using it correctly) is irrelevant. – Zac Howland Jan 15 '14 at 21:49
  • @jalf If you've ever written an application using MFC, you used a variation of the singleton pattern (likely without even realizing it). Additionally, it is a logical fallacy to assert that because you cannot think of a good reason for something, that none exists. Lack of knowledge does not prove non-existence. – Zac Howland Jan 15 '14 at 21:51
  • @ZacHowland: Perhaps it is irrelevant to the question, that's right, but it is educative - and "almost always" is still "almost always". If there are corner cases where it is actually OK to use Singleton, for some definition of "OK", that's something that should be left to experienced professionals that know what they do very well. Giving an OP on SO the advice to drop Singleton is a good attitude. Also (I know you didn't address this at me, but I feel like commenting on it), the fact that we were forced by MFC to use certain patterns doesn't make those patterns good. – Andy Prowl Jan 15 '14 at 21:59
  • @AndyProwl I think you mistake my advocacy of proper use of a given design pattern for advocacy that it is always a good choice. My point to Etienne was simply that if he is asserting that it is **never** a good choice, he is no more correct than someone who advocates that it is **always** a good choice. Just because you haven't used your Philips-Head screwdriver in years does not mean you should take it out of your toolbox. The wording of patterns being "good" or "bad" is frustrating. There is nothing inherently good (or bad) about a given design pattern, but rather how it is utilized. – Zac Howland Jan 15 '14 at 22:04
  • 2
    (cont) And since the OP never stated how this class is being used (much less whether he is actually using a singleton design pattern), there is no basis (only assumptions) to make that suggestion. It would be no different than suggesting he should use Java instead of C++. – Zac Howland Jan 15 '14 at 22:08
  • @ZacHowland: Singleton inherently violates the Single Responsibility Principle, because it mixes the responsibility of a type with the responsibility of creating just one instance of that type. Violating the SRP is bad for the reasons we all know and there is no "correct way" of violating SRP. Something that violates SRP simply shouldn't be used. Granted, I can't prove that there are no corner cases whatsoever where one may have a valid reason to violate the SRP, but unless you know there is a valid reason (and the OP 99.99% does not have one), it is fine to suggest not to use Singleton. – Andy Prowl Jan 15 '14 at 22:15
  • @AndyProwl It only violates SRP if implemented to do so (again, an assumption). It can be done (e.g. using a factory) without violating SRP. Once again, we're back to making assumptions about the OP's architecture and projecting suggestions that have absolutely nothing to do with his actual problem. – Zac Howland Jan 15 '14 at 22:24
  • @ZacHowland: I disagree, but I don't want to make this thread even longer than it already is. Thank you for sharing your opinion :) – Andy Prowl Jan 15 '14 at 22:30
  • @AndyProwl As a side note, there was a long discussion about this very same topic several years ago: http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons While there are some that share your view, there are also some who share [mine](http://stackoverflow.com/a/138010/529761) :) – Zac Howland Jan 15 '14 at 22:37
  • @Zac I have used code that relied on singletons before. That does not prove that singletons were the correct design choice for those, just that it is what they used, and therefore users of that code were stuck with it – jalf Jan 16 '14 at 08:31
  • @ZacHowland How is it a logical fallacy to say "until you can come up with one single counterexample, I'm assuming none exists", but it is apparently not a fallacy to say "I can't think of a single example where this is useful, but I KNOW IT IS!"? – jalf Jan 16 '14 at 08:31
  • @ZacHowland heck, how is is it not a logical fallacy to claim that "there is no possible way in which *any* design pattern can be universally good or universally bad"? – jalf Jan 16 '14 at 08:41
  • @jalf [Your logical fallacy is "burden of proof"](https://yourlogicalfallacyis.com/burden-of-proof) - by saying, "I can't think of a reason to do X, therefor none exists unless you can show otherwise" is an example of that logical fallacy. And your last statement is an example of a [strawman](https://yourlogicalfallacyis.com/strawman) argument. – Zac Howland Jan 16 '14 at 15:06
  • @ZacHowland I'm not saying "I have proven that no good uses exist". I *am* saying that "until someone can *show* me a good use for a singleton, I am going to *assume* that no good use exists". And frankly, it is the only sane position to take. All you have to do is produce *one* simple counterexample. It is very reasonable. If your position is "singletons are not useless", then I would expect you to show me an example of a case where they are not useless. If you can't do that, if *you* have never seen singletons being useful, then I have no reason to believe your claim. – jalf Jan 16 '14 at 17:22
  • @ZacHowland Have you considered that **you** are the one making the claim? You are the one saying "this code construction is useful". By default, if I bang random code together, it is *not* useful. You claim that a certain construction differs from this default. You are making the claim. I am saying "I'll believe it when I see it". The logical fallacy is *yours*, and the burden of proof is on *you*. – jalf Jan 16 '14 at 17:23
  • @ZacHowland and how is it a strawman argument to refer to the exact claim that you made? You said, and I quote: "There is nothing inherently good (or bad) about a given design pattern". And I summarized this position as "there is no possible way in which *any* design pattern can be universally good or universally bad". If my interpretation is different from what you meant, please enlighten me as to how I understood you. But despite what you may think, referring to what you actually *said* is not a "strawman argument". – jalf Jan 16 '14 at 17:28
  • @jalf You are attempting to play with semantics. You are not just assuming that no good use case exists, but also advocating against its use because you feel no good use case exists. That is different than stating, "I do not know of a good use case (period)." I never said it was or wasn't useful (putting words in my mouth does not help your logic). I simply said it is a tool in your toolbox, and not to discount it simply because people have a tendency to misuse it. So no, the burden of proof is not on me (re-read the description of that logical fallacy). – Zac Howland Jan 16 '14 at 19:31
  • @jalf The very definition of a strawman argument is the *misrepresenting* of someone else's argument in order to make it easier to counter. That is exactly what you are doing. Design patterns are tools. It isn't the tool that is good or bad, but how it is used. (which is **exactly** what I said, if you had actually quoted the whole thing). If you had referred to what I actually said, instead of trying to paraphrase it incorrectly, your point is moot; hence, the strawman argument. – Zac Howland Jan 16 '14 at 19:33

3 Answers3

0

You are effectively trying to take a runtime decision and make it at compile time. For this simple example, you can do it simply by casting the enumeration value to an int

myValue = static_cast<int>(pathValue);

But the better approach would probably be to create a constructor that takes a parameter:

Foo(const Paths& path)
{
    if(path == Paths::PATH_ONE)
    {
         // do something
    }
    else if(path == Paths::PATH_TWO)
    {
         // do something else
    }
}

In which case, your class no longer needs to be a template:

class Foo : public SomeParent { ... };

Another solution would be to specialize the template for the values, but you would need 2 class declarations for that.

Zac Howland
  • 15,149
  • 1
  • 23
  • 37
0

One usual metaprogramming way to solve such problems (independently of having a singleton class or not!) goes as follows:

enum Selections
{
    PATH_ONE ,
    PATH_TWO ,
};

class ImplBase : public ImplBase
{
    // Declare an interface and common implementation bits
}

class ImplBaseOne : public ImplBase
{
    // Implement details for PATH_ONE
};

class ImplBaseTwo
{
    // Implement details for PATH_TWO
};

template<Selections Choice>
struct Selector
{
     typedef void Result; // This should result in a compiler error for 
                          // invalid/unknown specializations
};

template<>
struct Selector<PATH_ONE>
{
     typedef ImplBaseOne Result;
};

template<>
struct Selector<PATH_ONE>
{
     typedef ImplBaseOne Result;
};

template<>
struct Selector<PATH_TWO>
{
     typedef ImplBaseTwo Result;
};

template<Selections Choice>
class ExposedClass : public Selector<Choice>::Result
{
    // ...
}
πάντα ῥεῖ
  • 83,259
  • 13
  • 96
  • 175
0

You have several way to do what you want at compile time which turn into template specialization:

1. Just for values

template<Paths> struct MyValue;

template<> struct MyValue<Paths::PATH_ONE> { static constexpr int value = 11; };
template<> struct MyValue<Paths::PATH_TWO> { static constexpr int value = 22; };

And so you have:

Foo() { myValue = MyValue<pathValue>::value; }

2. For functions

You may create several functions/method parametrized

template<Paths> void MyAssignVariable(int& var);

template<> void MyAssignVariable<Paths::PATH_ONE>(int& var) { var = 11; };
template<> void MyAssignVariable<Paths::PATH_TWO>(int& var) { var = 22; };

And so you have:

Foo() { MyAssignVariable<pathValue>(myValue); }

And as partial specialization is not possible on function, the following is preferred:

template<Paths> struct PathSpecialization;

template<> struct PathSpecialization<Paths::PATH_ONE> {
    static void MyAssignVariable(int& variable) { variable = 11; };
    // You may add other methods here.
};
template<> struct PathSpecialization<Paths::PATH_TWO> {
    static void MyAssignVariable(int& variable) { variable = 22; };
    // You may add other methods here.
};

And so you have:

Foo() { PathSpecialization<pathValue>::MyAssignVariable(myValue); }
Jarod42
  • 173,454
  • 13
  • 146
  • 250