Sign in to follow this  
iLoveGameProgramming

Unity Singleton and template Error (C++)

Recommended Posts

Hello, First of all thank you for taking time to help.

I recently started playing around with templates and singletons. I'm trying to make something like Unity's Debug.Log but in C++. Starting with simple things like cout. Unfortunately I got stuck and with all the googling I can't seem to find how to solve this issue without not making class singleton. Maybe there is better way of doing this?

 

//Console.h

#pragma once
#include <iostream>

class Console
{
private:
	Console() {}
	~Console() {}
public:
	static Console& instance()
	{
		static Console INSTANCE;
		return INSTANCE;
	}

	template <class T>
	void Print(T arg);

};

//Console.cpp

#include "Console.h"

template <class T>
void Console::Print(T arg)
{
	std::cout << arg.c_str() << std::endl;
}

//main.cpp

#include "Console.h"
int main(int argc, char *argv[])
{
	Console::instance().Print(5);
	return 0;
}

The error is:

Error 1 error LNK2019: unresolved external symbol "public: void __thiscall Console::Print<int>(int)" (??$Print@H@Console@@QAEXH@Z) referenced in function _main E:\WORK\InLine2D\InLine2D\InLine2D\main.obj InLine2D
Error 2 error LNK1120: 1 unresolved externals E:\WORK\InLine2D\InLine2D\Debug\InLine2D.exe 1 1 InLine2D
 
Edited by Konrad Jablonski

Share this post


Link to post
Share on other sites
Unless you explicitly list out all template instantiations, you cannot separate the template into header and source files.

See also: Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?


That aside, you shouldn't use globals because blah blah blah.

EDIT:
SeanMiddleditch has a valid point Edited by fastcall22

Share this post


Link to post
Share on other sites

Unless you explicitly list out all template instantiations, you cannot separate the template into header and source files.

See also: http://www.parashift.com/c++-faq/templates-defn-vs-decl.html

 

Thank you, this makes much more sense now. I swear I had it in one place at one point, but I was getting other errors and thought it was because of that. Just changed it to one file and it's all working now. It's my first time using templates by the way, trying to learn more C++.  Thank you.

Edited by Konrad Jablonski

Share this post


Link to post
Share on other sites


The singleton, a type that can only have one instance ever created, where attempting to create more than one is forbidden by code, that is something to avoid. Even on consoles still a terrible idea. Even for logging where some people suggest it is good, it is a terrible idea: I've worked on plenty of projects where I wanted a private logging system.


A well-known instance, sometimes implemented as pointers within a globally readable data structure, is sometimes a compromise made in coding design.

These well-known instances are NOT singletons (you can create multiple instances if needed) and they are not globals (statically allocated objects), but they are dynamically allocated objects with well-known pointer locations, and they come with strict rules about who can use them for what. These also need to have carefully reviewed, vigorously enforced policies to ensure they are not misused, treating them as short-lived constants in almost all places, with well-defined times of when they can change, such as outside of Update() loops.

Sometimes these well-known instances are managed through a single global object that is nothing more than a bunch of pointers. So you could access it through ::Systems.audio->method(), but there are some big concerns about it. If you are careful that they are mutable and immutable at the right times -- that is they are not modified by using them and that they are only modified at specific times, then it can sometimes work out.

The worst bugs are the ones where those values are modified at the wrong time or by systems unknown. Without warning the behavior of the game is suddenly different, and you cannot find who modified what, when, or why. Suddenly a state was set or a value was modified, and you don't know which of the many systems that could have modified it did so in the wrong spot. Those are nightmares, and the reason globals and mutable shared state are considered terrible design generally.

Share this post


Link to post
Share on other sites

These well-known instances are NOT singletons


I appreciate the distinction and I agree with frob.

I'm used to using "singleton" in a sense that is probably not the same as what the Gang of Four originally described it as. smile.png

See the service locator pattern (also also here) for a better description of what I usually call a "singleton" and what (I think) frob is talking about.

Share this post


Link to post
Share on other sites
For further example, the "singletons" I prefer look something like this:

class IResourceMgr {
public:
  virtual ~IResourceMgr() = default;

  static IResourceMgr& Instance();
  static void ResetInstance(std::unique_ptr<IResourceMgr> instance = nullptr);

  virtual Load() = 0;
  virtual Foo() = 0;
  virtual Bar() = 0;
};
Most user code accesses the above just like the regular singleton pattern prescribes (hence why I call it such), but you can still control the lifetime.

You can pass around any sort of IResourceMgr as you see fit (if you need local temporary, for example), you are still in absolute control of lifetime, you can override the implementation for mocks and testing, and you can select to use a stack-based approach (PushInstance and PopInstance, roughly) when it makes sense to do so.

Share this post


Link to post
Share on other sites

Just be aware, service locators and well-known instances can be a source of nasty bugs and serious design flaws.

 

The pattern introduces quite a lot of coupling. The coupling makes it more difficult to reuse code, where passing a parameter would have allowed reuse. The coupling has a high risk of violating several SOLID principles, it generally breaks Open/Closed, and has a high risk of breaking interface segregation and dependency inversion, but smarter developers will follow them by policy.

 

The key feature that absolutely must be followed if you want to preserve your sanity is that their behavior and use must be immutable. Any of the services being referenced must not be liable to change. They absolutely cannot change while they are in use. Functionality they control should be gated (both by policy and code rules) to prevent inappropriate changes.

 

It is generally best to pass parameters directly to methods rather than rely on these tools. There are usually better design decisions away from the patterns but those better solutions require more parameters, more thought, more insight, and more time to implement.

 

 

 

Again, the service locator / well-known instance pattern is NOT A GOOD DESIGN generally.  It is an unfortunate compromise taken when design is bad, when other programmers incorrectly coupled features, and when you cannot take the time to break the couplings properly.  It should not be the first tool you reach for. It is an unfortunate crutch, a compromise made due to tight deadlines and the sad reality that too much code, both design and implementation, seriously stinks. 

 

It is not a thing to be proud of.  It is a thing that you are best removing from the code as quickly as possible.  It is a code smell, a technical debt, not a point of glory.

Share this post


Link to post
Share on other sites

It is not a thing to be proud of.  It is a thing that you are best removing from the code as quickly as possible.  It is a code smell, a technical debt, not a point of glory.


And here's where I disagree. That kind of purity sounds nice on paper but is academic and just doesn't work in practice. Down that path lies passing 60 parameters to constructors and grotesque contortions just to avoid using a global here and there. All of your code because harder to read, harder to understand, and harder to maintain.

Software inherently relies on layered services. Your OS syscalls are, for all intents and purposes, services/singletons. Your hardware is a service. When you build layered software you add more of your own. There's a point at which software design and implementation simply gets more manageable when you assume layer A sits on top of layer B and don't force a decoupling where there _is_ an intrinsic and unavoidable coupling; good use of globals/singletons/services can accomplish that with no _actual_ problems (despite all the hypothetical ones you might come up with).

Dogmatic application of pithy principles gets you nowhere. At least, nowhere you want to be. I've been there. smile.png

Share this post


Link to post
Share on other sites

I have to agree with Sean here, I am seriously tired of dealing with the cognitive overhead of dependency injection frameworks that have me forced to pass the same damn object around everywhere because it's sufficiently basic and low-level that everything needs it, and at the end of the day it's still a global because everything everywhere has a reference to the exact same object. Things are just as bad as with a global, except now I have a dependency injection framework with it and my code's simple logic is obscured by a weird meta-language to describe its relationship with other simple dependencies (parameter-passing in constructors/factories, annotations, XML dependency trees; pick your poison). I do not see a problem with having genuine services as long as their interface is sane, nor am I against explicitly codifying abstract dependencies when it makes sense and adds value. I have a brain and my colleagues do as well, and at work I find that most of it is being used working with against "modern" design patterns and contorting straightforward code to fit someone else's idea of "decoupled code" instead of actually implementing business logic, and probably producing far more bugs as a result. As everything else in life, design principles become bad and dangerous when taken in excess. If in doubt, stop and think.

 

Anyway frob I found it interesting that you used the word "generally" followed by an unconditional statement. What in your opinion would be an uncommon use case where a singleton or service locator would be a sensible design choice?

Share this post


Link to post
Share on other sites

I have to agree with Sean here, I am seriously tired of dealing with the cognitive overhead of dependency injection frameworks that have me forced to pass the same damn object around everywhere because it's sufficiently basic and low-level that everything needs it, and at the end of the day it's still a global because everything everywhere has a reference to the exact same object. Things are just as bad as with a global, except now I have a dependency injection framework with it and my code's simple logic is obscured by a weird meta-language to describe its relationship with other simple dependencies (parameter-passing in constructors/factories, annotations, XML dependency trees; pick your poison). I do not see a problem with having genuine services as long as their interface is sane, nor am I against explicitly codifying abstract dependencies when it makes sense and adds value. I have a brain and my colleagues do as well, and at work I find that most of it is being used working with against "modern" design patterns and contorting straightforward code to fit someone else's idea of "decoupled code" instead of actually implementing business logic, and probably producing far more bugs as a result. As everything else in life, design principles become bad and dangerous when taken in excess. If in doubt, stop and think.

 

Anyway frob I found it interesting that you used the word "generally" followed by an unconditional statement. What in your opinion would be an uncommon use case where a singleton or service locator would be a sensible design choice?

 

Completely agree.

Share this post


Link to post
Share on other sites


Error 2 error LNK1120: 1 unresolved externals E:\WORK\InLine2D\InLine2D\Debug\InLine2D.exe 1 1 InLine2D

Anyway, back to the OP's question.

 

Whenever you're trying to do something in C++, it's always a good idea to look to see how the standard C++ already does it, because there have been a lot of expert input into the design and implementation of the standard C++ library.

 

In the standard C++ library, there is a global console logging object with a set of overloaded and templatized functions for inserting into it.  In fact, you're trying to create a similar Java-style singleton class to wrap the standard object that already does exactly what you need, except not in the style a Java develop mey be accustomed to, with static member functions returning object instances that are used to invoke instanced functions with overloaded procedures for combining formatting and streaming functions.

 

In the C++ standard library you will find a set of global logging stream objects that are designed to be instantiated on first use:  std::cout, std::cerr, and std::clog.  They have an accompanying suite of overloaded and templatized functions (operator<<()) that perform manipulation and insertion into those logging streams.  They have already solved your problems, consider using them instead of wrapping them in your own problematic code, or at least examine their design and emulate it yourself.

 

The C++ standard library logging objects are designed to be extended not only by overloading operator<<() for your own types but by replacing their streambuf to give you control over things like final destination or setting categories and severities.  Notice the post linked to there was addressing pretty much exactly the same topic as this one, and is dated 2006.  The aswer is still completely valid over a decade later.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Forum Statistics

    • Total Topics
      628284
    • Total Posts
      2981829
  • Similar Content

    • By ForgedInteractive


      Who We Are
      We are Forged Interactive, a small team of like-minded game developers with the sole purpose of making games we love! We're a team of artists, animators, programmers, level designers, writers, composers, producers, and other creative minds. We want to make games that you, the modern gamer want to play! We hope to build a community that enjoys our games as much as we love creating them. With your feedback and support we will be able to achieve that.

      About the Game
      GAME NAME is a fun, action-packed army builder with unique characters, challenges and engaging levels. Set forth on an adventure to protect friends, family and countrymen from new adversaries. Once defeated your enemies turn coat and join you in your adventures. Players can enjoy a range of troops and abilities based on their gameplay style which become more important as maps introduce more challenging terrain, enemies and bosses. Strong orc knights, dangerous shamans, and even a dragon are out on the prowl. Knowing when to fight and when to run, and how to manage your army is essential. Your actions alone decide the fate of this world.

      Previous Work by Team
      Although we are working towards our first game as a team, our team members themselves have past experience in the industry.
      This includes members who have worked on titles including:
      Final Fantasy Kingsglaive, FIFA, Xcom 2 and Civilization.

      Who are we looking for? 3D Modellers Concept Artists Marketing Specialists Level Designer

      What do we expect? Reference work or portfolio. Examples what have you already done and what projects you have worked on academic or otherwise. The ability to commit to the project on a regular basis. If you are going on a two-week trip, we don't mind, but it would be good if you could commit 10+ hours to the project each week. Willingness to work with a royalty based compensation model, you will be paid when the game launches. Openness to learning new tools and techniques
      What can we offer? Continuous support and availability from our side. You have the ability to give design input, and creative say in the development of the game. Shown in credits on websites, in-game and more. Insight and contacts from within the Industry.
      Contact
      If you are interested in knowing more or joining. Please email or PM us on Skype. Myself or Colin will reply to you within 48 hours.

      E-mail: Recruitment@ForgedInteractive.com
      Skype: ForgedInteractive

      Regards,
      David and Colin

      Follow us on:

      Facebook: https://www.facebook.com/ForgedInteractive/
      Twitter: @ForgedInteract
      Youtube: https://www.youtube.com/channel/UCpK..._as=subscriber
      Reddit: https://www.reddit.com/user/Forged_Interactive/
    • By Eck
      I just saw their courses were knocked down to $10 each and figured I'd share the info here. They have stuff for Unity, Unreal, drawing, business, etc. I haven't used their stuff before, but the previews I looked at seemed pretty good and there is a user review system as well.
      https://www.udemy.com/courses/search/?q=Unity&src=ukw
      - Eck
       
    • By zizulot
      first and only logo , for now
    • By sidbhati32
      I am working on a game in which we control a rectangular box at the bottom of the screen. Three sphere which has alphabets in it fall down. When the game starts, a word is generated from the predefined list of words(which I'll give) and we are supposed to touch the correct sphere having the alphabet based on that word. The question is how to detect if I have touched the correct sphere. 
      secondly, if I have touched a correct sphere before and there is no recurrence of that alphabet in that word then during the second wave the game should not proceed if I touch the same alphabet again.
      Looking forward to your answers, i have to submit this project in a couple of days. please help! (Working on Unity 3D)
      Thanks
    • By NDraskovic
      Hey guys,   As the title says, I'm trying to control a desktop game by using my mobile phone as a controller.  I created two scenes, one that acts as a server, other as a client.    Server has this code: void Start () {         Test = "Nothing yet happened";         NetworkServer.Listen(25000);         NetworkServer.RegisterHandler(888, ServerReceiveMessage);     }         private void ServerReceiveMessage(NetworkMessage message)     {                 StringMessage msg = new StringMessage();         msg.value = message.ReadMessage<StringMessage>().value;         if (!String.IsNullOrEmpty(msg.value))         {             Test = "Message received";             string[] deltas = msg.value.Split('|');             Horizontal = Convert.ToSingle(deltas[0]);             Vertical = Convert.ToSingle(deltas[1]);             TestScript.MoveForward(Vertical);             TestScript.RotateAroundY(Horizontal);         }         else         {             Test = "Nothing received";         }     }  
        And client this:  private void Connect()     {              client.Connect(IPAddress, 25000);           }     void Start () {         client = new NetworkClient();         Connect();            }         void Update () {    #if UNITY_ANDROID         MobileTouches = Input.touches;         if (MobileTouches.Length > 0)         {             for (int i = 0; i < MobileTouches.Length; i++)             {                 if (MobileTouches[i].phase == TouchPhase.Moved)                 {                     Horizontal = MobileTouches[i].deltaPosition.x;                     Vertical = MobileTouches[i].deltaPosition.y;                 }else if(MobileTouches[i].phase == TouchPhase.Stationary)                 {                     Connect();                                  }             }         } #elif UNITY_EDITOR               Horizontal = Input.GetAxis("Horizontal");         Vertical = Input.GetAxis("Vertical"); #endif         thumb.Translate(Vector3.up * Vertical * Time.deltaTime);         thumb.Translate(Vector3.right * Horizontal * Time.deltaTime);         SendControllerInfo();     }     static public void SendControllerInfo()     {         if (client.isConnected)         {             StringMessage msg = new StringMessage();             msg.value = Horizontal + "|" + Vertical;             client.Send(888, msg);         }     }  
        Ip address is hard coded, I just replaced it with the "IpAddress" variable. The code itself builds fine, and when I try to run in on a desktop computer, it works as expected (just a simple movement of an object on the server screen). However when I try to publish the client scene to a mobile device (Android), it doesn't connect to the server. They are both connected to the same network. Can anyone tell me what the problem might be?   Thanks
  • Popular Now