20 Jan, 2010, Crelin wrote in the 1st comment:
Votes: 0
Well here's my sitiuation, I'm running out of bits for certain variables such as my TRIG_ variables for m/r/oprogs.

I'm using Rom2.4b6 and as a small fix to add one more available bit, I added ff by doubling ee and marking it unsigned

#define ee			1073741824
#define ff 2147483648u


but that only gives me one more usable bit….so are there any better methods known or available to have more usable bits?

I know I could probably add a new set of variable defines for trigs and give them another line in the area files, which I'm thinking I may have to do but just curious if anyone knows of any other quicker/efficient methods?
20 Jan, 2010, Scandum wrote in the 2nd comment:
Votes: 0
Instead of an int you can use a long long, and use:

#define bit33 (1LL << 32)

Should give you 64 bits to work with on most platforms.
20 Jan, 2010, Davion wrote in the 3rd comment:
Votes: 0
Scandum said:
Instead of an int you can use a long long, and use:

#define bit33 (1LL << 32)

Should give you 64 bits to work with on most platforms.


Keep in mind that any variable you use with this (for example, affected_by) must also be a long long in order for this to work.
20 Jan, 2010, Crelin wrote in the 4th comment:
Votes: 0
if I were to do this…how would I define the new bits…

i.e.

#define gg (1LL << 32)
#define hh ??

would hh be (2LL << 32) or (1LL << 33)

may seem like a newbie question, going to look up some documentation on long longs as well.
20 Jan, 2010, elanthis wrote in the 5th comment:
Votes: 0
(1LL << 33)

Let me explain briefly. << is the left shift operator. So if you have the 8-bit number 00000001 (which is the same as decimal 1) then shifting it left once (<< 1) will result in 00000010. Shifting it left twice will result in 00000100. Etc. The result is that shifting 1 by some number N will result in the (N+1)th bit being set. That is, << 1 sets the second bit (0010) while << 2 sets the third bit (0100). Shifting by 0 does not change the number, leaving 1 as a simple 0001, e.g. the first bit.

The big numbers you see in the existing code's bitflags are the decimal representations of those kinds of binaries numbers (that is, binary numbers with only a single bit set to on). Binary 00000001 is decimal 1, binary 00000010 is decimal 2, binary 00000100 is decimal 4… binary 10000000 is decimal 128. They form the sequence of all positive powers of two, just like a decimal number composed of a 1 followed by some number of 0s forms the sequence of all positive powers of 10.

I would replace those bitflags in the existing code with the left-shift operator version Scandum showed you. It's significantly cleaner and easier to understand.

So far as long long, it's pretty simple. It's just an integer guaranteed to be at least 64-bits wide. That's about it. I usually recommend that people use stdint.h and its types int8_t through int64_t and their unsigned counterparts, uint8_t through uint64_t, and the good ol' size_t. They make your code more portable, the code's data dependencies clearer, and size_t in particular can help avoid a small assortment of sundry errors and warnings.
20 Jan, 2010, Crelin wrote in the 6th comment:
Votes: 0
alright, so just to double check…replace it with something like…

#define a (1LL << 0)
#define b (1LL << 1)
#define c (1LL << 2)


and redefine any variable that uses these bits to a long long?
20 Jan, 2010, elanthis wrote in the 7th comment:
Votes: 0
That does it. I would HIGHLY recommend at least making a typedef for bitset_t or the like.

typedef uint64_t bitset_t;

/* or */

typedef unsigned long long bitset_t;


And use that instead of just blasting long long all over. It makes your code cleaner.
20 Jan, 2010, Sharmair wrote in the 8th comment:
Votes: 0
First, I would not use long long as it is not standard, and not supported by all systems.
A lot of times, it does make sense to add a new flag set. It make more sense adding
a new set when the flags are not tightly related (meaning as a group, not just each
flag). In your case though, they are quite tightly related as in an empty flag set means
there are no program types.
If I was you I would get a copy of SMAUG 1.4 or newer and use the extended bitvector
system (actually, I would be using SMAUG and not ROM in the first place). The extended
bitvector system is pretty self contained and is not hard to use in other codebases
(I used it in a circlemud before, and that differs a lot more from SMAUG then ROM or
any other merc deriv).
20 Jan, 2010, quixadhal wrote in the 9th comment:
Votes: 0
If you want to use arbitrary number of bits, in C, and don't mind having them be in structures, everyone always forgets the bitfield.

struct {
dark : 1;
damp: 1;
deep: 1;
depressing: 1;
/* Add 712 more down here */
} room_bits;

There are a couple of gotchas, like you can't have pointers directly to the bits, and mixing them in with regular variables will force word-alignment and probably waste space, but they work.

Of course, for MUD code in general, the biggest gotcha is the bad practice (IMO) of checking multiple bit conditions at once. if(bitfoo & (POO_FLAG|P_FLAG)) then do_stuff(); That won't work. But for sane uses, if(ch->act & ACT_AGGRESSIVE) isn't much different than if(ch->actbits.aggressive), is it?
20 Jan, 2010, Kline wrote in the 10th comment:
Votes: 0
If you can manage to compile in g++ (a feat in itself for most older Diku-based code) you can just use std::bitset and avoid the whole mess of non-portable number types or hacked together systems like EXT_BV.
20 Jan, 2010, elanthis wrote in the 11th comment:
Votes: 0
Without C++ and without bitfields, you can still get arbitrarily long sets of bits with a few simple macros. All you need is a char array that has enough bits and a few macros to take any bit index and convert it into the array index and the remainder bit inside that specific char element. Something like:

/* helper macros */
#define BIT_INDEX(b) ((b) / 8)
#define BIT_REMAINDER(b) (1 << ((b) % 8))

/* declare a new bit array; c is max number of bits, n is name of array */
#define BIT_ARRAY(c, n) char n[BIT_INDEX(c - 1)] + 1]

/* check or change bits in a bit array; a is bit array to use, b is bit number to check */
#define IS_BIT(a, b) ((a)[BIT_INDEX(b)] & BIT_REMAINDER(b))
#define SET_BIT(a, b) ((a)[BIT_INDEX(b)] |= BIT_REMAINDER(b))
#define CLEAR_BIT(a, b) ((a)[BIT_INDEX(b)] &= ~BIT_REMAINDER(b))


I would replace the last set of three macros with functions (inlined if you want and your compiler supports it).

KaVir had some reasons or another against bitfields a couple years back that we got into an argument about. I think it came to portability issues, which I don't really agree with, but then I see no merit Sharmair's claims that long long might cause portability headaches either. The chances that you will ever be using a compiler that doesn't support bitfields or long long is pretty low, given that both have been available since at least the introduction of the now 11-year-old C99 specification. C++ doesn't technically have long long, but every single compiler supports it anyway, and it's being added in C++0x. The only ancient compiler I ever see people using these days are the Cygwin people who accidentally install GCC 3.4, which is an easy fix. :)

One thing I _do_ find bad about bitfields, though, is the lack of the ability to encapsulate them. With any other approach to bit flags, you can wrap all accesses behind functions (or at least macros), while bitfields require you to expose the bitfield publicly. Even in pure C code that can bite you in the ass sometimes. I would use real functions just for the simple fact that it makes it possible to log or break much easier on bit set manipulations. Having witnessed first hand the mind-boggling productivity gain that a simple real-time in-game debugging interface can give level designers, there's no way I'd pass up on the ability to print a message to any builder whenever any bit flag changes on a mob/item (if the builder opts in). Usually it'd be information overload but sometimes it makes figuring out why objects are getting into a particular state insanely easy and gets rid of the need to go find a developer to run things in a debugger and track down a potentially very difficult to replicate bug. Likewise, any changes in any other property should be able to be printed out to the builder, as well as allowing state inspection. Being able to turn on an extended display to show key state info whenever the mob is displayed (e.g., "the wolf [state:ATK life:12 tgt:you]") is also pretty awesome. In a graphical game you'd do this with a combination of a separate debug console, a debug draw interface (where you can overlay arrows and wireframes and bounding circles and such over the actors in-game), and a debug text interface (show text boxes or something similar hovering above/beside the actors in-game). With a MUD that doesn't support multiple output windows via MUSHclient or the like, you've got to try to cram all that information into the main text stream, which kind of sucks, but with care you can pull it off.
21 Jan, 2010, KaVir wrote in the 12th comment:
Votes: 0
elanthis said:
KaVir had some reasons or another against bitfields a couple years back that we got into an argument about. I think it came to portability issues, which I don't really agree with, but then I see no merit Sharmair's claims that long long might cause portability headaches either. The chances that you will ever be using a compiler that doesn't support bitfields or long long is pretty low, given that both have been available since at least the introduction of the now 11-year-old C99 specification.

The problem with bit fields isn't lack of support, it's the fact that the ordering of bits is architecture dependent and memory padding rules vary from compiler to compiler.

If you're using C++, why not use a std::vector<bool>?
21 Jan, 2010, elanthis wrote in the 13th comment:
Votes: 0
Because most people want to drag std::vector<bool> out back and shoot it, with good cause. Use std::bitset<> instead. You really don't need the size of the 'container' to change during runtime in these cases, anyway.
21 Jan, 2010, KaVir wrote in the 14th comment:
Votes: 0
He's using these bits for mobprogs, so it's not beyond the realm of possibility that he might want to resize during runtime, depending on how flexible his mobprog system is. Otherwise, std::bitset would also be fine. If he's using C, then an array of unsigned char would be my choice (as it's the only data type guaranteed to have no padding bits). I really wouldn't touch bit fields with a barge pole though; as stated by K&R, "Almost everything about fields is implementation-dependent".
21 Jan, 2010, quixadhal wrote in the 15th comment:
Votes: 0
KaVir said:
The problem with bit fields isn't lack of support, it's the fact that the ordering of bits is architecture dependent and memory padding rules vary from compiler to compiler.


True enough, but unless you're saving binary data, why would it matter? Most folks these days use one form or another of ascii data files to store their world data, characters, etc… To me, (room->flags & ROOM_DARK) == room->flags.dark == IsDark(room). Nothing outside of the loading/saving code should care that it happens to be bit 13 today.
21 Jan, 2010, KaVir wrote in the 16th comment:
Votes: 0
quixadhal said:
True enough, but unless you're saving binary data, why would it matter? Most folks these days use one form or another of ascii data files to store their world data, characters, etc…

Many muds save their files as plain text, true, but they usually still use numeric values for bit vectors. If you used bit fields you couldn't do that - you'd need to do something else, like read/write a separate field for each bit (i.e., instead of saving a 32 bit integer for the "MOB AI" bits, you'd save 32 separate fields each followed by a boolean value).

That does have certain advantages (eg, more readable, easier to remove unused bits, easier to 'grep' files for certain flags, etc) but it also has drawbacks (eg, less efficient, more work to maintain, files would likely become much bigger, etc). I admit that I do save quite a bit of data this way myself, so I'm not against the idea. But it won't be the right solution for everyone.
21 Jan, 2010, Tonitrus wrote in the 17th comment:
Votes: 0
Since no one else has said it (that I've seen) I'd just like to suggest that the entire concept of bitfields should be taken out and shot and everyone should just use bools or ints. You waste a bunch of memory, but who cares, memory is much cheaper than processors, and accessing ints/bools is a lot faster than accessing bitfields, due to memory alignment issues and whatnot.

More importantly, bitfields are a pain, and ints/bools are easier to deal with.

I realize that it'd probably be Too Much Trouble ™ to convert over a system that already uses bitfields, but someone had to say it.
21 Jan, 2010, quixadhal wrote in the 18th comment:
Votes: 0
KaVir said:
(i.e., instead of saving a 32 bit integer for the "MOB AI" bits, you'd save 32 separate fields each followed by a boolean value)


Actually, as a database guy, I use the same idea that one uses for junction tables. A value is true by virtue of it existing, and false otherwise. So, if you have a room that's set to be DARK and SILENT, the entry would literally be: "Flags DARK SILENT\n"
21 Jan, 2010, David Haley wrote in the 19th comment:
Votes: 0
Tonitrus said:
accessing ints/bools is a lot faster than accessing bitfields, due to memory alignment issues and whatnot.

Err… you might want to justify or even rethink this claim a little bit. For starters, if your bit vector is implemented with units the size of the alignment chunk (or however you'd like to put it) there are no alignment issues; to get the individual bit out you need apply only a single bit-wise operation. If bools are one byte in size and not aligned, you will run into alignment issues there.

Tonitrus said:
More importantly, bitfields are a pain, and ints/bools are easier to deal with.

That's what encapsulation is for…

quixadhal said:
Actually, as a database guy, I use the same idea that one uses for junction tables. A value is true by virtue of it existing, and false otherwise.

AKA negation as failure: something is true if you find it, and false if you can't find it (or otherwise derive its existence). An interesting logical model for databases (and also interestingly quite different from what most people might think of as logic; just because you can't prove that "X" is true doesn't mean that "X" is false).
21 Jan, 2010, elanthis wrote in the 20th comment:
Votes: 0
Quote
He's using these bits for mobprogs, so it's not beyond the realm of possibility that he might want to resize during runtime, depending on how flexible his mobprog system is. Otherwise, std::bitset would also be fine.


So use boost::dynamic_bitset or a custom implementation (which are dead easy to write).

Avoid std::vector<bool>. The entire semantics of it might even change in a future C++ release, as the committee is slowly coming around to the idea of losing backward compatibility and just fixing the major ****up they made with the weird specialization of std::vector<bool>. Keeping in mind that bool is the size of an int, then not only would that potential change break some corner-cases of the interface, but your bit set memory usage would increase by 32x on most platforms.

Tonitrus said:
Since no one else has said it (that I've seen) I'd just like to suggest that the entire concept of bitfields should be taken out and shot and everyone should just use bools or ints. You waste a bunch of memory, but who cares, memory is much cheaper than processors, and accessing ints/bools is a lot faster than accessing bitfields, due to memory alignment issues and whatnot.


Bzzt. Try again. Aside from the fact that a good compiler (you know, those written many, many years after K&R's latest revision of the C book) will optimize bit field access as well as you could optimize it by hand, know that using up more memory than you need to makes your code slower on modern CPUs. Cache misses are only getting more and more expensive with each iteration of hardware technology. Things are getting close to the point where it's actually faster for an OS to do on-the-fly compression/decompression of memory, because the extra CPU overhead of the compression is less than the cost of the extra cache misses without the compression.

Also, what David said. ints aren't even guaranteed to be aligned on all platforms. You increase your memory usage 32x (or more) for no appreciable gain on modern systems.

It amuses me and saddens me at the same time just how many programmers are totally clueless when it comes to optimizing code. Most programmers – even the ones who just started learning how to program very recently – keep on using advice and tricks from the early 90s which not only fail to help on modern CPUs, but in some cases actually make things _worse_. I watched a student – who had never programmed a line of code before in his life before a year and a half ago – implement lookup tables for trig functions in some code he was "optimizing," because he read that's how id made the Quake engine really fast before consumer 3D hardware even existed. Of course, on a modern architecture, not only do the lookup tables have way less precision than just performing the calculation, the overhead of the memory fetch is actually slower than just calculating the trig function.

I also still see people use float instead of double thinking it's faster (floats have been treated as doubles by the FPU on most CPUs for years now) for simple calculations, but in reality the only time you really want to use float over double is when you're storing a bunch of them in memory, like say for a complex 3D model's vertices. I still see people inlining every function they can despite the fact that the cache pressure outweighs the cost of a function invocation in most non-trivial programs, to the point that compiling with -Os can often result in noticeably better performance than compiling with -O3 on certain large apps.

And then you get the people who are using binary formats for speed instead of using a well-designed text format. One of my professors for example keeps advocating complex serialization frameworks that can handle both XML and a custom binary format, one for development/debugging and one for release-mode. Naturally, the binary version is faster, but not noticeably so in any but the largest of files, and simply using a less retarded text format (e.g. JSON) can really close the performance gap. Or the people who hand-unroll loops and make their code impossible to read despite that a good compiler has excellent loop-unrolling optimization passes built-in. Or the people who write very complex hand-tuned assembly that is faster than the general C code on one specific CPU but ends up being slower on all the others, including that CPU's eventual successor. Or the people who rewrite Python/Lua/Ruby code portions in C "for speed" when the real problem was that their algorithms sucked and their 5x speed gain with the C code is still crap when compared to the 300x speed gain they could get without even touching C.

Quote
More importantly, bitfields are a pain, and ints/bools are easier to deal with.


That is entirely a subjective matter. foo.bar = 1 is easier in many respects than foo.flags[BAR] = 1. The only case the bitfield code is measurably worse is when you need to iterate over the bits; if that's a necessity, then bitfields just don't cut it, especially with the unpredictable padding issues.

That said, while I do use bitfields in places, I would never use bitfields for a public API. I don't care if it's a bit less typing; optimizing for the amount of typing necessary is the stupidest thing you can ever optimize for as a programmer. The less you have to type, in general, the less expressive the code is down the road and the less flexible the code is (when comparing code in the same language, at least). If you like code that is compact, I suggest you stick to IOCCC submissions; you can't get any "better" than that.

I seriously would use a function to set/check/clear bits. It's not that much more typing, but it lets you do all kinds of extra debug things whenever you set bits. That's really useful. Far, far, far more useful than saving yourself a few keystrokes. Even if you're using a std::bitset or what not for your implementation, _do not expose that in the public interface_.
0.0/62