Jump to content

  • Log In with Google      Sign In   
  • Create Account

Way to pre-declare standard templated class types?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 Servant of the Lord   Crossbones+   -  Reputation: 19499

Like
0Likes
Like

Posted 14 March 2013 - 10:14 PM

For compile time optimizations, I'd like a way to pre-declare std::string, and some common container classes I use alot. Specifically, std::vector<std::string>, and to a lesser extant, std::map<std::string, std::string>.

 

Since I can't go like this:

namespace std { class string; }

(Because std::string is really the templated type std::basic_string<char>)

 

I was wondering what you gentlemen think about something like this:

class MyString; //Pre-declaration in the headers that need it.
 
void myFunc(MyString &myString);
 
//Later, the actual definition:
class MyString : public std::string { };

 

Since my entire code-base would follow this usage of std::string and std::vector<std::string>, the direct conversion of std::string into MyString wouldn't be a problem. Though, if it was, I could make MyString have an explicit std::string constructor and implicit std::string castability, and when C++11's constructor inheritance is added to MinGW (in the upcoming 4.8), do that.

 

 

On a scale of 1-10, how much does this make you want to hang me? laugh.png

 

Are there any ramifications or side-effects I'm not realizing, in doing this?


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


Sponsor:

#2 Cornstalks   Crossbones+   -  Reputation: 6985

Like
3Likes
Like

Posted 14 March 2013 - 10:59 PM

I'm sure you know you these standard classes don't have virtual destructors, and I'm sure you know this means you can't delete a polymorphic pointer to your MyString. People often bring this up as a huge issue when it comes to inheriting from standard classes, and while it's entirely valid, I don't think it's a realistic problem for any half-sane programmer. So in short, there isn't a real technical problem.

 

However, I want to hang you on a scale of about 6. Reducing build times is great and all, but I'd personally rather have longer build times than using custom classes simply for the sake of forward declaration.

 

Particularly, MyString uses camel casing, unlike any of its member functions. I hate that inconsistency. And sure, you can provide functions so I can use std::string instead of MyString, but still, I'd personally prefer MyString to serve more of a purpose than simply forward declaration. If that's its only purpose, I'd prefer the longer build times and not use it.


[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#3 Trienco   Crossbones+   -  Reputation: 2170

Like
1Likes
Like

Posted 14 March 2013 - 11:11 PM

Usually precompiled headers are brought up a lot with template heavy code to reduce compile times. Is that not an option or are build times still too long?


f@dzhttp://festini.device-zero.de

#4 RobTheBloke   Crossbones+   -  Reputation: 2342

Like
1Likes
Like

Posted 15 March 2013 - 06:50 AM

Why not just forward declare std::string?

 

namespace std
{
template < typename T > struct char_traits;
template < typename T > class allocator;
template
<
    typename charT,
    typename traits,
    typename Alloc
> class basic_string;

typedef basic_string<char, char_traits<char>, allocator<char> > string;
}
 
// type now valid
const std::string* ptr = 0;

// before including the actual header files
#include <string>

 

the code above was tested with gcc 4.42. Your implementation may vary



#5 SiCrane   Moderators   -  Reputation: 9594

Like
2Likes
Like

Posted 15 March 2013 - 07:27 AM

Doing so is non-portable. Adding a declaration to namespace std is undefined behavior according to the C++ standard, which includes forward declarations of standard library classes.

#6 swiftcoder   Senior Moderators   -  Reputation: 9987

Like
1Likes
Like

Posted 15 March 2013 - 10:50 AM

Are there any ramifications or side-effects I'm not realizing, in doing this?

What on earth are you trying to accomplish with this ungodly hack?

 

 - If you are worried about compile times, a pre-compiled header should fix that.

 - If you are worried about typing, a typedef should fix that.

 - If you are trying to solve circular dependencies... SAY WHAT?


Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#7 Servant of the Lord   Crossbones+   -  Reputation: 19499

Like
0Likes
Like

Posted 15 March 2013 - 12:01 PM

I'm not worried about typing, I prefer names like 'std::string' over names like 'string'.

And it's definitely not circular dependencies. laugh.png

 

It's just compile times - I've tried pre-compiled headers, several times, but every time so far, the compiler (MinGW) would sporadically crash halfway through the build (the compiler itself, not the compiled code), and it'd take down the IDE (QtCreator) with it. QtCreator has support for pre-compiled headers, and I followed the QtCreator documentation for pre-compiled headers with MinGW. It just was unstable. When it worked (every 2 out of 3 compiles), it greatly reduced compile times. But when it failed, I had to End Task mingw32-make and then restart my IDE - which was very upsetting to my workflow.

 

I hadn't considered the lack of virtual destructors actually, but I hadn't intended to use these polymorphically. 

 

I didn't really want to rename std::string anyway - it makes it harder for others to read the code. I do have tons (several dozen) of std::string helper functions in a separate namespace (String::blah()), but as much as I'd like them in some String class for convenience, it'd make the class too much of a monolith, so they're probably better off as standalone functions.

 

I'll try RobTheBloke's method - even if it's non-standard, if it works with MinGW and GCC on most platforms, I'd be fine with it. Is the worse case scenario (on sane compilers), a failure to compile or is it likely that something harder to detect would result? Maybe accidentally declaring new 'standard' classes (but without definitions) by mistake?

 

I wonder if GCC has a non-standard built-in pre-declaration header somewhere that it uses internally - I'll look around.


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#8 swiftcoder   Senior Moderators   -  Reputation: 9987

Like
0Likes
Like

Posted 15 March 2013 - 12:57 PM

the compiler (MinGW)

I think I found your problem blink.png

 

But in all seriousness, I assume you are using a recent (4.7.2) build of GCC?


Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#9 Servant of the Lord   Crossbones+   -  Reputation: 19499

Like
0Likes
Like

Posted 15 March 2013 - 03:10 PM

Yep:

 

g++ (Built by MinGW-builds project) 4.7.2
Copyright © 2012 Free Software Foundation, Inc.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#10 RobTheBloke   Crossbones+   -  Reputation: 2342

Like
1Likes
Like

Posted 15 March 2013 - 05:24 PM

Doing so is non-portable.

 

The same is also true of #pragma once, un-named unions/structs, SIMD optimisations, fread/fwrite, graphics API's, sound API's, input API's, wide strings, C++ 0x, C++ 11, GUI code, and more or less everything that most game developers use on a daily basis. This is a game developer forum, not comp.lang.c++. If you want program truly portable games, you're choices are: text adventures, and text adventures. If you have any aspirations beyond that, well you're just going to have to accept that your codebase will contain a number of platform specific headers. Your compiler may vary, as will the implementation of the standard library, hence the caveat I posted above. 

 

 

Adding a declaration to namespace std is undefined behavior according to the C++ standard, which includes forward declarations of standard library classes.

 

Yes, technically speaking I could write a compiler in which the std lib is implemented as intrinsics, rather than C++ which is treated as a typical library. I doubt you would use it, and I doubt anyone else would either (me included). If the standard lib provided with your compiler is written in C++, it follows the rules of C++. Since forward declarations are legal in C++, it will obey those rules. Or do you disagree?

 

 

Is the worse case scenario (on sane compilers), a failure to compile or is it likely that something harder to detect would result?

 

Failure to compile is the worst that can happen. Annoying, but fixable.

 

I hadn't considered the lack of virtual destructors actually, but I hadn't intended to use these polymorphically.

 

If all you're doing data wise is this:

 

struct String : public std::string
{
   int pod_data_only;
};

 

there is no problem. If you're doing this:

 

struct String : public std::string
{
   std::vector< std::map< std::string, std::vector<int> > > dynamic_data;
};

 

Then be afraid! be very afraid!

 

- If you are worried about compile times, a pre-compiled header should fix that.

Not always an option. You need a compiler that supports them (PS3? Wii?).

 

 

- If you are trying to solve circular dependencies... SAY WHAT?

It happens, especially when working in large teams. It shouldn't happen, and there is usually a way to fix them 'properly', but when deadlines are tight, it's usually better to get it out of the door no matter what, than refactor vasts swathes of code (and go through another round of QA/bug-fixing/delayed release).


Edited by RobTheBloke, 15 March 2013 - 05:32 PM.


#11 Servant of the Lord   Crossbones+   -  Reputation: 19499

Like
0Likes
Like

Posted 15 March 2013 - 05:51 PM

I wonder if GCC has a non-standard built-in pre-declaration header somewhere that it uses internally - I'll look around.

 

<bits/strfwd.h> in GCC and MinGW pre-declares std::string, std::wstring, std::u16string, std::u32string. Though it says:
 

/** @file bits/stringfwd.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{string}
*/

 
Based on RobTheBloke's code a few posts up, this is the header I'm going to start using: (works with MinGW 4.7.2)

#ifndef COMMON_SYSTEM_FWDSTRING_H
#define COMMON_SYSTEM_FWDSTRING_H

/*
	Forward-declares std::string, StringList, and StringMap.
	
	WARNING: This is non-standard.
*/

namespace std
{
	//----------------------------------------------------------------------
	//	Forward-declare std::string (and std::char_traits, and std::allocator)
	
	template < class T > struct char_traits;
	template < typename T > class allocator;
	template < typename CharT, typename Traits, typename Alloc = allocator<CharT> > class basic_string;

	typedef basic_string<char,		char_traits<char>,		allocator<char> >		string;
	typedef basic_string<wchar_t,	char_traits<wchar_t>,	allocator<wchar_t> >	wstring;
	typedef basic_string<char16_t,	char_traits<char16_t>,	allocator<char16_t> >	u16string;
	typedef basic_string<char32_t,	char_traits<char32_t>,	allocator<char32_t> >	u32string;

	//----------------------------------------------------------------------
	//	Forward-declare std::vector.

	template < class T, class Alloc > class vector;
	
	//----------------------------------------------------------------------
	//	Forward-declare std::less, std::pair, and std::map.

	template <class T> struct less;
	template <class T1, class T2> struct pair;

	template < class Key, class T, class Compare, class Alloc > class map;
	
	//----------------------------------------------------------------------
}

typedef std::vector<std::string, std::allocator<std::string> > StringList;
typedef std::map<std::string, std::string, std::less<std::string>, std::allocator<std::pair<std::string, std::string> > > StringMap;

#endif // COMMON_SYSTEM_FWDSTRING_H

 
GCC will lets me pre-declare the templated classes, but won't let me have default template arguments, because later when the actual templated class is defined, it complains about "redefinition of default argument", which is why I manually declared std::basic_string<char, char_traits<char>, allocator<char> >, instead of just std::basic_string<char>.


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#12 swiftcoder   Senior Moderators   -  Reputation: 9987

Like
0Likes
Like

Posted 15 March 2013 - 08:04 PM


- If you are trying to solve circular dependencies... SAY WHAT?

It happens, especially when working in large teams. It shouldn't happen, and there is usually a way to fix them 'properly', but when deadlines are tight, it's usually better to get it out of the door no matter what, than refactor vasts swathes of code (and go through another round of QA/bug-fixing/delayed release).


I was specifically referencing circular dependencies with respect to the standard library - you can't really cause that without editing your standard library headers...

Tristam MacDonald - Software Engineer @Amazon - [swiftcoding]


#13 wintertime   Members   -  Reputation: 1708

Like
2Likes
Like

Posted 16 March 2013 - 04:07 PM

#include <iosfwd>

That is at least standard and you would get the string indirectly too.

 

For everything else just try to move as many includes into the .cpp files and remove not needed and redundant headers that get included indirectly already.

Then look if you can not show some of your own types in some headers if you only need references or pointers.

Then maybe use abstract base classes and only include all low level stuff in a derived class.

Oh and try to not include windows.h in your header files and define that WIN32_LEAN_AND_MEAN before that so there is less things included.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS