Login [Register]
Don't have an account? Register now to chat, post, use our tools, and much more.
Just curious, what is it about that last example, theelitenoob, that is an interesting C / C++ tip? Seems like a pretty standard setup to me.

EDIT: a few quite simple things for now, more of hitting on GCC extensions that a surprising number of people here don't know exist. I'll hit on them just because I want to contribute something for now Razz I'll do something better later.

Label tricks in GCC

GCC has a very cool extension that few other compilers have: the ability to refer to labels as actual memory addresses, the ability to jump to arbitrary memory addresses, and using these to do some otherwise impossible things in C-standard complying setups.

To get the memory address value of a label, you can do so like this:


Code:
label:

...

void*ptr = &&label;


The && operator returns the address casted as a void*. You can also jump to arbitrary addresses, as long as they are typecasted as void*:


Code:
int main(int argc, char**argv) {
   ...
   goto (void*)main;
   return EXIT_SUCCESS;
}


Another possible trick that is cool is the ability to find the size, in bytes, of an actual function!


Code:
void do_something(void) {
   printf("%s!", "Hello your name is Cemetech");
   do_something_END:
}

...

   printf("size of function do_something: %i", (int)(&&do_something_END - (int)do_something));



Casting to unions in GCC

If you are assigning a viable value of an allowable type to a union structure, you can actually do one of two things. Using this union:


Code:
union cemetech {
   uint32_t hello;
   uint16_t goodbye;
   union nikky {
     char*isgod;
     void*kermdoof;
   };
} fun;


you can declare certain members the usual way, like:


Code:
fun.hello = 5;
fun.nikky.isgod = "nope.";


But GCC allows you to typecast to union for compatible types:


Code:
fun = (union cemetech) 5; // same as fun.hello = 5
fun.nikky = (union cemetech) "nope.";
// the above can also be done with even less given info:
fun = (union cemetech) "nope.";


Seemingly useless at first, but very good for storing a type into a union, when you want to anonymously store data without having to access particular elements of the union explicitly.
Those are some neat tricks, Ashbad.

If you find yourself stuck in a C environment for whatever reason, you needn't fear. You can still achieve OO functionality in C, just without the syntactical sugar.

You can get virtual member functions. That's really all the core of OO is. They look a bit like this:


Code:
#include<stdlib.h>
#define new(a) __##a##_ctor
#define delete(a) __##a##_dtor


struct foo{
    int bar;
    int (*get)( struct foo* this );
    int (*set)( struct foo* this, int var );
};
int __foo_get( struct foo* this )
{
    return this->bar;
}
int __foo_set( struct foo* this, int var )
{
    return this->bar = var;
}
struct foo* __foo_ctor( )
{
    struct foo* ret = malloc( sizeof( struct foo ) );
    ret->get = __foo_get;
    ret->set = __foo_set;
    return ret;
}
void __foo_dtor( struct foo* this )
{
    free( this );
}


int main(){

    struct foo* a = new( foo )();
    a->set( a, 92 );
    printf( "%i", a->get( a ) );
    delete( foo )(a);
}


Notice my defines for "new" and "delete"? They're pretty cute, eh?

You can also achieve encapsulation. All you need are multiple declarations of your struct.

For the implementation of your member functions, as well as friend classes and functions, they see this:

Code:
struct foo{
    int foo, bars, are, tasty;
};


However for the actual interface, you can describe it as:

Code:
struct foo{
    const void* private[sizeof(int)*4];
};


Using this method, you can also achieve varying levels of encapsulation by just maintaining varying struct declarations. You just have to be careful to keep things in the same order, and there's also a chance that your compiler might "optimize" your struct by rearranging things. If you use this method, it's safest to hide the entire struct or not at all.


There's also a bunch of fun things that you can do with macros.

Do you have a pesky ellipses? Don't want to make the end user pass in an "argnum" variable too? Well, you can use these macros:

Code:
#define NUMARGS(...) (sizeof((int*[]){0,__VA_ARGS__})/sizeof(int*)-1)
#define foo(...) _notadef_foo(NUMARGS(__VA_ARGS__),__VA_ARGS__)

int _notadef_foo( int argnum, ... ){
    va_list a;
    va_start( a, argnum );
    int t = argnum;
    while( argnum-- > 0 )
        printf( "%i\t", va_arg( a, int ) );
    va_end( a );
    return t;
}


Granted, it's not a replacement for a format string, but it's nice for when passing in arbitrary amounts of data. Also, since NUMARGS is a constant expression, it (at least should be) evaluated at compile time. At worst, it would be a division and a subtraction though, as sizeof is ALWAYS handled at compile time AFAIK.
Ashbad wrote:
Label tricks in GCC

GCC has a very cool extension that few other compilers have: the ability to refer to labels as actual memory addresses, the ability to jump to arbitrary memory addresses, and using these to do some otherwise impossible things in C-standard complying setups.

To get the memory address value of a label, you can do so like this:


Labels and gotos are not cool, they are terrible. Please forget they exist ASAP.

Quote:
Casting to unions in GCC

But GCC allows you to typecast to union for compatible types:

Seemingly useless at first, but very good for storing a type into a union, when you want to anonymously store data without having to access particular elements of the union explicitly.


It isn't actually a cast, but a constructor (see http://gcc.gnu.org/onlinedocs/gcc/Cast-to-Union.html )

Kind of neat-ish trick, but not really useful.

Kaslai wrote:
If you find yourself stuck in a C environment for whatever reason, you needn't fear. You can still achieve OO functionality in C, just without the syntactical sugar.

You can get virtual member functions. That's really all the core of OO is. They look a bit like this:


That is a very common C pattern. GTK uses it heavily, for example.

Quote:
There's also a bunch of fun things that you can do with macros.


You have to be extremely careful with them, though, and they are usually not what you should be reaching for.
Ashbad wrote:
Just curious, what is it about that last example, theelitenoob, that is an interesting C / C++ tip? Seems like a pretty standard setup to me.


Oh, it's standard? I thought it was something new. I never saw it done before so it kind of surprised me.
This isn't useful, more of something that would be cool to know if they ever made a Jeopardy for programmers:
Code:
#include <stdio.h>
#include <stdlib.h>

void main(int j) {
  printf("%d\n", j);
  (&main + (&exit - &main)*(j/1000))(j+1);
}
Has to be my favorite thing from StackOverflow, yet! Smile
I don't think you're quite confident on the meaning of "quandary:" it means a problem with no solution. This, on the other hand, is a problem with many solutions! You could use factorial() just as you did, or you could use an intermediate variable. Your factorial function is using recursion; that's one of the classical examples, other than the Fibonacci series, of writing recursive functions in C.
Kaslai wrote:
However for the actual interface, you can describe it as:

Code:
struct foo{
    const void* private[sizeof(int)*4];
};

If you want to hide all struct members, you don't even have to declare the whole struct. This suffices:

Code:
struct foo;
Then anything using "foo" won't be able to access any members (not even "private"), and they won't even be able to create foo objects without using "new(foo)".

If you still want to support virtual functions (such as "set" and "get" accessor methods), you could put only public methods and members at the beginning of the public interface struct and omit the private members altogether:

Interface:

Code:
struct foo{
    int (*getbar)( struct foo* this );
    int (*setbar)( struct foo* this, int var );
};

Implementation:

Code:
struct foo_impl{
    struct foo public;
    int bar;
};

In the implementation you can cast a struct foo* to struct foo_impl*, and any changes to struct foo (the public interface) will automatically "change" struct foo_impl (the private implementation's structure) to match. The downside to this specific technique is you first have to cast "this" to access any private members/methods:

Code:
int __foo_getbar(struct foo *this) {
    return ((struct foo_impl *)this)->bar;
}

Of course, that's easily fixed with another macro:

Code:
#define pthis ((struct foo_impl *)this) // private this

int __foo_getbar(struct foo *this) {
    return pthis->bar;
}




_player1537 wrote:
This isn't useful, more of something that would be cool to know if they ever made a Jeopardy for programmers:
Code:
#include <stdio.h>
#include <stdlib.h>

void main(int j) {
  printf("%d\n", j);
  (&main + (&exit - &main)*(j/1000))(j+1);
}
Has to be my favorite thing from StackOverflow, yet! Smile

Niiiiice! It took me a minute to figure out what that's doing and to realize that it wouldn't just totally crash under "normal" conditions (if you pass 2000 or more arguments, or a negative number of arguments, it'll crash).
I'm really interested to see that the first thing I posted in this thread had to do with double precision and latitude/longitude, because the tip I'm about to post is the same (but for printf/sprintf/snprintf).

Printf/Sprintf/Snprintf Doubles with Full Precision
If you try to use %f as-is, you'll find that some precision gets lost. Based heavily on this StackOverflow post, you can do the following:


Code:
#ifdef DBL_DECIMAL_DIG
  #define OP_DBL_Digs (DBL_DECIMAL_DIG)
#else 
  #ifdef DECIMAL_DIG
    #define OP_DBL_Digs (DECIMAL_DIG)
  #else 
    #define OP_DBL_Digs (DBL_DIG + 3)
  #endif
#endif

void myfunc(const double somearg) {
    //...
    printf("%.*f\n", OP_DBL_Digs, somearg);
    //...
How does it work? The %.*f formatting specifier in there actually takes two arguments instead of the usual one: the decimal precision of the number in digits, and the number itself. That macro stack gives you the proper number of digits for a double. Voila!
Kllrnohj wrote:
Labels and gotos are not cool, they are terrible. Please forget they exist ASAP.


I do not believe this is the correct approach to "outdated" features of the C language like this one. The problem is not that they're inherently bad features, the problem is that they can easily be misused and so you end up with uglier code than what you began with.

One of the few valid uses of goto and labels is using it to break out of multiple nested loops with minimal performance hit. You will remember that in Java, you could do the following:


Code:

forBreakout:
for(int i = 0; i < arr.length; i++) {
  for(int j = 0; j < arr[i].length; j++) {
       if (arr[i][j] == 1) break forBreakout;
  }
}


C/C++ doesn't have that feature. You could do the following, which might incur a performance cost:


Code:

int breakout = 0;
int arrSize = sizeof(arr)/sizeof(arr[0]);

for(int i = 0; i < arrSize ; i++) {
  int innerArrSize = sizeof(arr[i])/sizeof(arr[i][0]);
  for(int j = 0; j < innerArrSize; j++) {
       if (arr[i][j] == 1) breakout = 1;
  }
  if(breakout) break;
}


But then, you will need to include

Code:

if(breakout) break;

all the way down, which might be tedious if there are many nested for loops.

So, to simplify the code, we could just apply a valid use case for the goto keyword:

Code:

int arrSize = sizeof(arr)/sizeof(arr[0]);
for(int i = 0; i < arrSize; i++) {
  int innerArrSize = sizeof(arr[i])/sizeof(arr[i][0]);
  for(int j = 0; j < innerArrSize; j++) {
       if (arr[i][j] == 1) goto forBreakout;
  }
}
forBreakout:
// code continues


The attitude is not condemnation, it is awareness. Smile
In 15 or so years of programming, the only times I've ever encountered situations where I felt that goto/label usage was appropriate in C/C++ code was in implementing lexer/parser constructs by hand, that are essentially verbatim translations of FSM transition graphs into code, and even still, it's usually a single label. A better implementation in most cases is embedding a switch(){} within a loop.

As far as C/C++ tips of the day go, I recommend reading the following (most of them are the first in a series, which you should continue through) on metaprogramming:


Other tips:

  • Smart pointers have come a long way. The vast majority of new code should be using std::unique_ptr<> to automatically free objects when they leave scope, (and using std::move if you need to transfer ownership), and reference types anywhere you want it to be impossible to get a nullptr or don't want to be responsible for an object's ownership.
  • Boost has some awesome header-only libraries. boost::variant provides a type-safe union, boost::optional provides an out-of-band/typesafe method to return a flag value when a function call fails to create a value of a type you were expecting, and boost::tribool provides three-state logic when you want to be able to reason about true/false/unknown values.
elfprince13 wrote:
In 15 or so years of programming, the only times I've ever encountered situations where I felt that goto/label usage was appropriate in C/C++ code was in implementing lexer/parser constructs by hand,


There are a few situations where a goto may be warranted (but not necessary). Error handling is one of them - when you have a function that can fail at certain steps and the failure need to be handled in a certain way.

Another situation is breaking out of nested loops.



Code:

for(i=255; i-->0;)
   for(j=255; j-->0;)
      for(k=255; k-->0;)
         {
         if(droid[i][j][k] == THE_ONE_WE_ARE_LOOKING_FOR)
            goto we_found_them;
         }
no_droid_found:
...
we_found_them:
...
[/code]
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 2 of 2
» All times are GMT - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement