[Solved]Name confusion

For help with general CEGUI usage:
- Questions about the usage of CEGUI and its features, if not explained in the documentation.
- Problems with the CMAKE configuration or problems occuring during the build process/compilation.
- Errors or unexpected behaviour.

Moderators: CEGUI MVP, CEGUI Team

GrubbyFun
Just popping in
Just popping in
Posts: 3
Joined: Sat Nov 20, 2010 05:43

[Solved]Name confusion

Postby GrubbyFun » Sat Nov 20, 2010 05:48

Hi every one!
I am a newer to CEGUI.
when I read the API of CEGUI, I find the follow description:
"Normal members should have the prefix d_, such as in d_thisVariable, while static members should have the prefix s_, such as in s_anotherVariable. "

ya, I understand "s_" is short for "static", but what "d_" short for?
why don't use "m_" instead?

Thanks a lot for any information!
Last edited by GrubbyFun on Mon Nov 22, 2010 03:19, edited 1 time in total.

User avatar
Kulik
CEGUI Team
Posts: 1382
Joined: Mon Jul 26, 2010 18:47
Location: Czech Republic
Contact:

Re: Name confusion

Postby Kulik » Sat Nov 20, 2010 06:16

I believe d_ is for data but that's just me. I was completely confused about this but it's just a matter of getting used to it. I already use so many conventions in other projects that it doesn't hurt that much :-D

If this were my call I would go for mVariable 8)

GrubbyFun
Just popping in
Just popping in
Posts: 3
Joined: Sat Nov 20, 2010 05:43

Re: Name confusion

Postby GrubbyFun » Sat Nov 20, 2010 09:52

Kulik wrote:I believe d_ is for data but that's just me. I was completely confused about this but it's just a matter of getting used to it. I already use so many conventions in other projects that it doesn't hurt that much :-D

If this were my call I would go for mVariable 8)


thanks Kulik. :D .

User avatar
Mikademus
CEGUI MVP
CEGUI MVP
Posts: 130
Joined: Wed Mar 18, 2009 19:14

Re: Name confusion

Postby Mikademus » Sat Nov 20, 2010 12:38

Oooo! Code conventions question!

First, questions about how to structure your code will inevitably result in forum wars. Be warned! :)

Now, the first question you should ask is not "what are the rules?" but rather "how will I make my code as understandable as possible?". If you hold this question in your mind then the rules people will suggest to you will start to make sense. And sometimes other people's suggestions will be nonsensical because they focus on rules in themselves rather than on rules to generate understandable code.

The "d_Something" and "mSomething" conventions are bout using prefixes to signal that there is something special about the variables, in this case they are "data" (d_) and "members" (mSomething). However, if you name your variables well this convention is usually superfluous. The only prefixes I myself use are "p" and "r" to indicate pointers and references (pMonster and rSpaceship), and "g_" to signify a global variable (g_pRenderSystem).

If you're interested, have a look at my own code standards at http://wiki.dark-omen.org/do/Wartbed:Code_style

:pint:

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Re: Name confusion

Postby CrazyEddie » Sat Nov 20, 2010 13:55

Yeah, it basically means data. These days I tend to agree that such warts are somewhat superfluous.

CE

GrubbyFun
Just popping in
Just popping in
Posts: 3
Joined: Sat Nov 20, 2010 05:43

Re: Name confusion

Postby GrubbyFun » Sun Nov 21, 2010 15:51

Thanks every one :D

Jamarr
CEGUI MVP
CEGUI MVP
Posts: 812
Joined: Tue Jun 03, 2008 23:59
Location: USA

Re: [Solved]Name confusion

Postby Jamarr » Mon Nov 22, 2010 21:50

Mikademus wrote:If you're interested, have a look at my own code standards at http://wiki.dark-omen.org/do/Wartbed:Code_style


I like most of this. Especially that you followed KISS with your standards documentation. Mine is about 4x the length and only contributes slightly more information lol. I really need to do some condensing. I would note that the example code is a bit too small/faint for my eyes; I have to zoom the page to make it legible. There are a few styles I do differently:

I prefer tabs for scope-indentation and use spaces for formatting/alignment within that scope. This lets developers use their preferred scope/spacing relationship while maintaining a consistent format/layout. I reject the idea that coding-style be dependent on software limitations, such as unconfigurable tab spacing. If I cannot configure an editor to my content, I can find something else.

On const and pointer/reference placement: I feel that placing const before the type, and the pointer/reference with the type, follows natural language semantics whereas the opposing syntax does not. It also maintains consistency for "return type" vs "variable type" declarations since pointer/reference placement with the variable-name (here function-name) is no longer natural. I feel that the flexibility allowed in the standard in these cases is actually a detriment to the language. In the case for pointer placement for variables, trading natural-language for less code is not an equivalent nor worthwhile exchange. And because of the pointer fiasco the language must then introduce the const placement fiasco. In my opinion the type-definition should include const and pointer qualifiers as part of that definition, because it follows the natural idea of "term => definition" aka "type => instance".

For enumerations I require them to be defined in their own parent container (struct/class). This is similar to prefixing all enumerations with a common identifier. Both approaches afford camel-cased naming without collision. One additional benefit to using containers is that the scope is limited to the container; thus the entire list of enumerations no longer pollute the global scope. Another benefit is that these containers allow you to extend enumerations, for instance customizing type-conversion through operator overloading; this can be especially useful for conversion to/from int and string.

All in all I think your coding philosophy is spot on ;)
If somebody helps you by replying to your thread, upvote him/her as a thanks! Make sure to include your CEGUI.log and everything you tried when posting! And remember that we are not magicians!

User avatar
Mikademus
CEGUI MVP
CEGUI MVP
Posts: 130
Joined: Wed Mar 18, 2009 19:14

Re: [Solved]Name confusion

Postby Mikademus » Wed Dec 08, 2010 19:20

Jamarr wrote:
Mikademus wrote:If you're interested, have a look at my own code standards at http://wiki.dark-omen.org/do/Wartbed:Code_style


I like most of this. ... All in all I think your coding philosophy is spot on ... Especially that you followed KISS with your standards documentation.

Thanks :pint: One thing most people who write or use code style specifications tend to misunderstand is that it is about rules. It is not. It is about understandable, expressive and easily maintainable code. To this rules can be of help and use, but just as with real language, sometimes you get your meaning across better through breaking rules, and flexibility is as important as following rules. Zealotry or puritanism about styles is not conductive to better code and rules tend to spawn religious conviction and opposition. Therefore my code standard is not really about rules but rather about principles and how these help produce better code.

On to specifics:

On const and pointer/reference placement: I feel that placing const before the type, and the pointer/reference with the type, follows natural language semantics whereas the opposing syntax does not. It also maintains consistency for "return type" vs "variable type" declarations since pointer/reference placement with the variable-name (here function-name) is no longer natural.

The reason I place const after the type is because I place things in order of importance for the reader. When we read signatures we first need to know the type and then the specifiers. Both the pointer *, the reference &, and the const keyword are specifiers to the type. The type is most important and then the specifiers follow. That the * and & have to be placed with the identifier rather than they type, though, is because the other way is actively misleading (again, the caninical example is "MyStruct* a, b;") and also gives rise to unnecessary dogmas like "though shalt have one and only one variable declaraed per line".

That the entire system of specifiers-for-types rather than singular-type-composites used in C/C++ may be suboptimal is an interesting discussion, but we have to deal with the cards we've been given :) Your alternative would probably solve quite a few syntactic gotchas in the language!

For enumerations I require them to be defined in their own parent container (struct/class). This is similar to prefixing all enumerations with a common identifier. Both approaches afford camel-cased naming without collision. One additional benefit to using containers is that the scope is limited to the container; thus the entire list of enumerations no longer pollute the global scope. Another benefit is that these containers allow you to extend enumerations, for instance customizing type-conversion through operator overloading; this can be especially useful for conversion to/from int and string.

Hmm. When dealing with namespaces I usually position them by how many clients they will have. Are they only used inside one class I place it in that class. Will it be used by clients in conjunction with other helpers or data for a class I place it in a namespace related to that class (f.i. the class "World" is accompanied by the namespace "world" that contains enums and utility classes and functions to World). However, In principle, I prefer namespaces to classes to collect related things, though the namespace model keeps things clean and structured systematic/thematic it does not have the extendability benefit of your struct-container approach. I haven't so far needed something like that, but will definitely keep it in mind!

It'd be cool if you could share your own style after revision! :)

Jamarr
CEGUI MVP
CEGUI MVP
Posts: 812
Joined: Tue Jun 03, 2008 23:59
Location: USA

Re: [Solved]Name confusion

Postby Jamarr » Wed Dec 08, 2010 20:43

That the entire system of specifiers-for-types rather than singular-type-composites used in C/C++ may be suboptimal is an interesting discussion, but we have to deal with the cards we've been given :) Your alternative would probably solve quite a few syntactic gotchas in the language!

Indeed, you are absolutely right. When it comes down to completing a project, you have to work with what you are given. I suppose the best compromise is to associate specifiers with variables, while actively pushing to slowly phase out semantic-shortcuts in favor of natural-language semantics.

Hmm. When dealing with namespaces I usually position them by how many clients they will have. Are they only used inside one class I place it in that class. Will it be used by clients in conjunction with other helpers or data for a class I place it in a namespace related to that class (f.i. the class "World" is accompanied by the namespace "world" that contains enums and utility classes and functions to World). However, In principle, I prefer namespaces to classes to collect related things, though the namespace model keeps things clean and structured systematic/thematic it does not have the extendability benefit of your struct-container approach. I haven't so far needed something like that, but will definitely keep it in mind!


You certainly bring up an interesting point with namespaces. The one issue here is that you have no control over namespace expansion, since namespaces can be expanded elsewhere in the code. Thus there is no guarantee that two distinct enums will not conflict, which is certainly possible in large projects. You may still end up having to prefix your enumerations, in which case you might as well enclose the enum in a struct/class instead. I suppose the one benefit is that you can use the "using" clause to eliminate, or shorten, the namespace qualifier. An example of extending enums would be forcing a default value when client code tries to forcefully use an invalid enum value. Another would be to print name of an enum, instead of it's value, when outputting streams.

It seems that c++0x will also try to address the known problems with enums. See Strongly Typed Enumerations. I must say, c++0x looks very exciting ;)

It'd be cool if you could share your own style after revision! :)


Hmm. Maybe ;)
If somebody helps you by replying to your thread, upvote him/her as a thanks! Make sure to include your CEGUI.log and everything you tried when posting! And remember that we are not magicians!

User avatar
Mikademus
CEGUI MVP
CEGUI MVP
Posts: 130
Joined: Wed Mar 18, 2009 19:14

Re: [Solved]Name confusion

Postby Mikademus » Thu Dec 09, 2010 14:36

Jamarr wrote:I suppose the best compromise is to associate specifiers with variables, while actively pushing to slowly phase out semantic-shortcuts in favor of natural-language semantics.

There is often an interesting tension between a language's programmability and its readability: languages that are natural-semantic to read also may tend to be arduous to write and may impose restrictions on how solutions can be expressed in code. C++ tends to side with flexibility over readability. I fully endorse this, but then I have the fluency of 20 years experience with it now, nonetheless I can still remember all the pitfalls of learning it. O.o C++ is an expert-friendly language, but unfortunately the discipline and foresight required by these experts to produce maintainable code aren't very common resources. This is not the language's fault though. :P

You certainly bring up an interesting point with namespaces. The one issue here is that you have no control over namespace expansion, since namespaces can be expanded elsewhere in the code. Thus there is no guarantee that two distinct enums will not conflict, which is certainly possible in large projects. You may still end up having to prefix your enumerations, in which case you might as well enclose the enum in a struct/class instead. I suppose the one benefit is that you can use the "using" clause to eliminate, or shorten, the namespace qualifier. An example of extending enums would be forcing a default value when client code tries to forcefully use an invalid enum value. Another would be to print name of an enum, instead of it's value, when outputting streams.

I'm thinking that using classes to contain enums might force some run-time overhead. Enums are compile-time constants, but (pure container) classes are instantiated on the stack. I don't know it an optimising compiler will elide this for classes only consisting of static or compile-time constant members. Also the semantic message sent by using a class as a namespace might be surprising when browing the code in that one expects classes to do more than just prefix enum names. These both points are just nit-picking and speculative though, and I realised I do use your style of enums-in-classes quite frequently, but usually in conjunction with storing the enum value(s), f.i. for representing settings or for serialisation.

It seems that c++0x will also try to address the known problems with enums. See Strongly Typed Enumerations. I must say, c++0x looks very exciting ;)

Definitely so! I'm very excited about it, and it will address a number of nagging kinks in the language! I'm a bit disappointed with that the Standard Committee had to cut out 'concepts' though, apparently for political reasons? My personal most-highly-anticipated features are the "auto" keyword and template aliases!

[edit]

...and Lambda Expressions! Hot damn, how could I forget that we finally get closures in C++!!!

Jamarr
CEGUI MVP
CEGUI MVP
Posts: 812
Joined: Tue Jun 03, 2008 23:59
Location: USA

Re: [Solved]Name confusion

Postby Jamarr » Fri Dec 10, 2010 01:49

Mikademus wrote:There is often an interesting tension between a language's programmability and its readability: languages that are natural-semantic to read also may tend to be arduous to write and may impose restrictions on how solutions can be expressed in code. C++ tends to side with flexibility over readability. I fully endorse this, but then I have the fluency of 20 years experience with it now, nonetheless I can still remember all the pitfalls of learning it. O.o C++ is an expert-friendly language, but unfortunately the discipline and foresight required by these experts to produce maintainable code aren't very common resources. This is not the language's fault though. :P


You are 13 years my senior, so I can respect that view point. I still feel that the standard, in this case, results in a negative ROI. Here is why: this semantic-shortcut allows one to define a combination of variables of type, const-type, pointer/reference-to-type, const-pointer-to-type, pointer/reference-to-const-type, and const-pointer-to-const-type. I cannot recall a single instance when this flexibility has ever been useful. The only time I can even recall seeing this flexibility used was in an educational setting. Under what circumstances or in what context is this ever useful? I do not think it is unreasonable of me to propose that this shortcut introduces unnecessary confusion to learning the language, to reading the language, and to understanding the language. And the trade-off is for, maybe, 1% of cases? This is of course in my experience, and I only have 7 years of c++ (~15y of "programming"). Two of my favorite ideologies come to mind:

Antoine de Saint Exupéry wrote:In anything at all, perfection is finally attained not when there is no longer anything to add, but when there is no longer anything to take away.


Obviously the language should be extended to allow programmers to solve more problems, or to solve existing problems in an easier fashion. My point with this is that in my experience only one solution is necessary, and often times the best solution is also the simplest solution. And that we should strive for simplicity.

Isaac Asimov wrote:Two is an impossible number, and can't exist. (this also known as the "zero, one, or infinity" rule.)


I know this is not 100% representative, since these are not the same type, they are however all bound to the same type. The premise here being, if you are using a type, const-type, pointer/reference-to-type, const-pointer-to-type, pointer/reference-to-const-type, or const-pointer-to-const-type, in what context do you ever need to use more than one of these definitions? In my experience rarely, if ever. I assume this to be the general case.

I'm thinking that using classes to contain enums might force some run-time overhead. Enums are compile-time constants, but (pure container) classes are instantiated on the stack. I don't know it an optimising compiler will elide this for classes only consisting of static or compile-time constant members. Also the semantic message sent by using a class as a namespace might be surprising when browing the code in that one expects classes to do more than just prefix enum names. These both points are just nit-picking and speculative though, and I realised I do use your style of enums-in-classes quite frequently, but usually in conjunction with storing the enum value(s), f.i. for representing settings or for serialisation.


Enums being compiler constants, I would assume compilers these days are clever enough to treat them as such even if they are defined within a class. It would be awkward, I think, since the compiler would likely have to write this exception into the compiler - and there would be no need to do so. Of course, I have not actually verified this. I also often use Enums in classes in conjunction with class members for setting/flag storage.

As for semantics, note that c++0x uses "enum class Name : type {}" syntax. It does seem awkward, even here. I am not sure why they decided to use "class" instead of "typename" here. Or maybe they will allow typename and class to be interchangeable, as they do in template declarations? I think the typename/class swap case is an even worse ROI than type specifiers. The allowance of two keywords for the same purpose is just silly. In both cases, the "typename" keyword conforms more to natural-language semantics.

Definitely so! I'm very excited about it, and it will address a number of nagging kinks in the language! I'm a bit disappointed with that the Standard Committee had to cut out 'concepts' though, apparently for political reasons? My personal most-highly-anticipated features are the "auto" keyword and template aliases!...and Lambda Expressions! Hot damn, how could I forget that we finally get closures in C++!!!


Indeed :pint:
If somebody helps you by replying to your thread, upvote him/her as a thanks! Make sure to include your CEGUI.log and everything you tried when posting! And remember that we are not magicians!

User avatar
Mikademus
CEGUI MVP
CEGUI MVP
Posts: 130
Joined: Wed Mar 18, 2009 19:14

Re: [Solved]Name confusion

Postby Mikademus » Sun Dec 12, 2010 00:26

Jamarr wrote:
Mikademus wrote:...C++ is an expert-friendly language, but unfortunately the discipline and foresight required by these experts to produce maintainable code aren't very common resources. This is not the language's fault though. :P

I still feel that the standard, in this case, results in a negative ROI. Here is why: this semantic-shortcut allows one to define a combination of variables of type, const-type, pointer/reference-to-type, const-pointer-to-type, pointer/reference-to-const-type, and const-pointer-to-const-type. I cannot recall a single instance when this flexibility has ever been useful. ... this shortcut introduces unnecessary confusion to learning the language, to reading the language, and to understanding the language. And the trade-off is for, maybe, 1% of cases? This is of course in my experience, and I only have 7 years of c++ (~15y of "programming"). ... The premise here being, if you are using a type, const-type, pointer/reference-to-type, const-pointer-to-type, pointer/reference-to-const-type, or const-pointer-to-const-type, in what context do you ever need to use more than one of these definitions? In my experience rarely, if ever. I assume this to be the general case. ... Obviously the language should be extended to allow programmers to solve more problems, or to solve existing problems in an easier fashion. My point with this is that in my experience only one solution is necessary, and often times the best solution is also the simplest solution. And that we should strive for simplicity.

I personally agree with this point willingly! C++ has such a superabundance of combinatory specifiers that apparently only serves to provide flexible ways to confuse everyone. As I am writing this, though, though my aesthetic preferences would be better satisfied by a cleaner model I can't deny two aspects that immediately jump through my temples and splatter my tea and keyboard:

Firstly, the original (and still tenuously upkept) ambition of C++ was to keep backwards feature-compatibility with C. The messy constructs we see come from building on a C model and over time adding things to align with that model (const, references etc). This was fantastic since it allowed C++ to have an enormous initial code base and was politically and pragmatically very savvy indeed, and it also had the unfortunate side effects we discuss now.

Secondly, even a stronger underlying core ambition of C++ is that the language will never decide for a developer how to represent a problem, and will provide all options for him to choose his own solution. And ethics aside, there is a number thing going on here: if a feature will be used in 1% of all solutions, and we have one million programmers coding in C++ (we have more), then that solution will be used 10,000 times in their collected set of sessions. Game producers often don't realise a similar thing, that a bug that only appears in 1% of all games will happen to thousands of gamers daily.

That said, yeah, const pointer to a const something yada yada can be bloody annoying and difficult to see the use of. I do use "mutable" and const_cast judiciously though so I have probably brainwashed myself quite thoroughly by now...

Antoine de Saint Exupéry's quote is a really good one! Mind if I finch it for my code style page? :P It is the perfect mindset to have when refactoring code! Seriously though. I think there isn't necessarily a conflict between it and a complicated and feature-rich language design: a rich set of features allows one to reduce code till it is as small as possible, but a too limited set of tools may force you to circumlocute rather than address directly. Probably why most languages grow as they do: LISP started rather clean and minimal and is now also feature-rich, Java was annoying as hell by a too restrictive design in the beginning, and is now unfortunately annoying as hell because it totally lacks any direction and coherence.

All in all, I think we are actually of a mind when it comes to simplicity as an ideal - I certainly know I'm partly justifying the complex feature set of C++ for the fun of the discussion! :)

As for semantics, note that c++0x uses "enum class Name : type {}" syntax. It does seem awkward, even here. I am not sure why they decided to use "class" instead of "typename" here. Or maybe they will allow typename and class to be interchangeable, as they do in template declarations? I think the typename/class swap case is an even worse ROI than type specifiers. The allowance of two keywords for the same purpose is just silly. In both cases, the "typename" keyword conforms more to natural-language semantics.

Good point! If they are consistent they should indeed allow both! I never use "class" in my templates, and though I saw it when templates was introduced into the language nowadays I actually never see anyone use anything but "typename". Better keywords to use might perhaps have been "static" (but I guess some bigwig on the commitee thought it too confusing because it implies a specific memory model) or "explicit", which already has the very meaning they intend for the strongly typed enums!

Mikademus wrote:My personal most-highly-anticipated features are the "auto" keyword and template aliases!...and Lambda Expressions! Hot damn, how could I forget that we finally get closures in C++!!!

Argh! I should have learned by now that mentioning things jinxes them! So we have all this wonderful candy in C++0x, and some of the most tasty pieces, like template aliases, aren't included in Microsoft's implementation!

(See list of compiler support for C++0x: http://wiki.apache.org/stdcxx/C++0xCompilerSupport)

X-(

Jamarr
CEGUI MVP
CEGUI MVP
Posts: 812
Joined: Tue Jun 03, 2008 23:59
Location: USA

Re: [Solved]Name confusion

Postby Jamarr » Mon Dec 13, 2010 20:35

Right. I should consider that low percentages can be misleading when dealing with large numbers ;) You do bring up an interesting point, in that an abundance of tools allows the wielder to derive the simplest solution within the toolset. However, there should be a balance of tools in the toolbox: too few and problems are harder to resolve, too many and the toolbox becomes cluttered and unwieldy. The balance is obviously subjective, and so discussions like these will always be abundant. I think that, in this case, both views are valid. The side you prefer is a matter of taste; taste being a matter of preference from one's experience that is refined over time ;)

It seems with all of Microsoft's product lines they burst into the scene with revolutionary ideas, granted many are 'borrowed' but at least they improve upon the original, and then they sit on their content asses waiting for everyone else to catch up. Eventually the open-market turtles it's way up and the process repeats. Looking around today, I believe we are approaching a better tomorrow; so long as the compiler market remains competitive and someone is pushing new standards, today being GCC, then we should start to see a brisker rate of progression ;)
If somebody helps you by replying to your thread, upvote him/her as a thanks! Make sure to include your CEGUI.log and everything you tried when posting! And remember that we are not magicians!

User avatar
Mikademus
CEGUI MVP
CEGUI MVP
Posts: 130
Joined: Wed Mar 18, 2009 19:14

Re: [Solved]Name confusion

Postby Mikademus » Thu Dec 16, 2010 15:45

About the size and clutter of tools in the toolbox,: yeah, there should be a balance. I actually think that C++ doesn't have too many tools to choose from, which is in fact testified to by that we have C++0x coming and a TR2 on the horizon after that. C++ grows slower than many languages, and I think Java, PHP and JavaScript are fine examples of languages that have grown fast, thrown in most everything they can into the language, and have lost the structure and intent of their first versions. C++ grows too slow for my taste, but the advantage is that most introductions are well incorporated into the language. Then I still agree with you that the type modifier syntax is a mess inherited from the C language model :D


I'd appreciate some feedback on some of my quirkier notions on code pedagogics and signalling of intentions :)

The first we've already discussed: putting modifiers after the type ("Object const &rObject; Sprite mutable *pSprite;".) This I consider makes code easier to scan and read because it places the important information first and also invites the positioning of the * or & with the identifier (which would actually help mitigate the confusion that Jamarr speaks of above.) Actually not quirky, I at least think it is very reasonable and justified.

Another quirk of mine that usually ticks off people is that I endorse using "struct" for simple classes or classes which are generally "open access", and "class" when data members are accessed and set through methods. Still I think it is justified because it signals an significant difference in intention and usage. The top two complaints tend to be C-veterans tend that dislikes inheriting from structs because it is counter to their expectations of struct in C, and OOP purists who think that it is silly to "use struct just to save some typing of keywords". It is my position that neither of these points are right since (1) C++ structs aren't C structs and in face are identical to classes only with the default access level set to public, and (2) it is not silly to save on boilerplate code, especially when the resulting source is easier to read and signals intended usage. Actually, when speaking OOP, using either protected or private inheritance is counter to "pure OOP" ideology since the class Derived can't "be-a" class Base, thus thwarting the goal of polymorphism (note: in the pure sense - I'm debating with purists here).

Cheers!


Return to “Help”

Who is online

Users browsing this forum: No registered users and 28 guests