Thread: NAQ: Everything you never wanted to know about CPP

  1. #16
    Sr. Software Engineer filker0's Avatar
    Join Date
    Sep 2005
    Location
    West Virginia
    Posts
    235
    Quote Originally Posted by Bubba
    OMG. We are like so your students. Please teach us while we soak up your vast knowledge. I'm trying to be as awesome as you at the pre-processor code but I know I'll never come close to your level. Your knowledge and breadth of information just amazes me. We bow before your greatness.
    I think you misunderstood what I intended with my comment that you quoted -- I was saying that everyone should make a point not to use, or misuse, the C processor (or any language environment feature) that might be very obscure. My use of "I" was not meant to be a gloat.

    But I can see where that came across as arrogant -- and for that, I'm sorry.
    Insert obnoxious but pithy remark here

  2. #17
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Of course, if you really want to go to town on it, then use m4 as your pre-processor.

    As far as I know, the original C pre-processor was just a specific case of m4.

  3. #18
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709
    Quote Originally Posted by Bubba
    Do you work for Microsoft? Because your code abuses the pre-processor about as much as theirs does. That is hideous.

    Imagine trying to track down the bugs. The compiler throws out some weird stuff and highlights the wrong line when the error comes from a line in a macro.

    Too many macros and the pre-processor is NOT for re-writing the language.
    Talking of which, have you seen the Source Engine... Source? (The engine Half Life 2 etc uses)

    I had a fiddle with the SDK the other day, and their (ab)use of the preprocessor extends my already well-grounded thoughts that Microsoft programmers are kidnapped at birth and subject to various experimental... oddities.
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  4. #19
    Registered User
    Join Date
    Aug 2001
    Posts
    244
    before abusing the preprocessor for writing c++ style code in c, the object oriented paradigma should be understood first. and then you will use c++.

    i guess most c++ programmers who had to write pure c code for some reason (e.g. assignments where it was mentioned explicitely that only pure c-code that compiles with --ansi --pedantic --Wall is accepted) tried to write c++ style code in c.

    at least i did that - but no solution looked as nice and familiar like the c++ code. and no solution was as nice as c++ solution would have been.
    thus when you want objects, inheritance, etc USE C++.
    if you dont want objects, use c.

    the only good reason for c is actually just for writing a c++ compiler in it, so it can be ported to any platform.
    (since the c language is rather easy to parse, and many c compilers follow the standard very closely)
    signature under construction

  5. #20
    Sr. Software Engineer filker0's Avatar
    Join Date
    Sep 2005
    Location
    West Virginia
    Posts
    235
    Quote Originally Posted by Raven Arkadon
    the only good reason for c is actually just for writing a c++ compiler in it
    Smile when you say that, padner! ( ) You're likely to start a holy war.
    Insert obnoxious but pithy remark here

  6. #21
    Registered User
    Join Date
    Aug 2001
    Posts
    244
    oops, c is also good for fast, platform independent algorithms of course.
    (even though c++ is in many cases faster than c, thanks to efficient implementations of the stl (and compiler optimizations))

    but since many ppl talk about game programming here, c++ would be definitely the better choice

    so the basic questions are simply:
    .) do objects provide any advantage?
    if your program is basically just the implementation of some algorithm, the answer is probably no.
    if your program is rather small and will stay small use either c or c++, both versions should be readable and easily maintainable. (e.g. a text adventure)

    if your program should grow big, use c++.
    (e.g. a game engine with different threads for keyboard/mouse input, network communication, rendering to using multiple render devices (like in ut: open gl, direct x, glide), and game logics (physics engine, scripts, hard coded game rules) and and and you are definitely better off with c++.

    .) when designing a library, it might happen, that the c++ is appropriate for the implementation, but the interface wouldnt have any advantage of c++ features.

    an example is the ode (a free physics engine) (www.ode.org), which is implemented in c++, but the public interface are c functions, because there wouldnt have been any advantage in using a c++ interface.
    i have to agree with that decision.

    .) do i have any advantage if i used the stl?
    if youre just working with arrays, the answer is probably no.
    but as soon as you want maps and sets the stl is probably a very good choice.

    .) c is easily portable since most compilers understand the "same" c language - while the c++ code accepted by different compilers varies in some important differences.
    e.g: the stl containers set and map under microsucks do not require the key-value to be a const.
    so with ms vc (at least the .net version i used) you could change the key-value and mess up the container.
    with the stl version that i have, the key-values are const! thus you cannot change them...
    so i had my code written under vc, and in order to port it to linux i had to rewrite parts of the program.
    (btw: in my eyes keys should be const - so ms did that wrong)
    okok, this is actually not the compilers fault, the implementation of the stl sucks.


    in my opinion: as soon as someone tries to write c++ style code in c, he would be better off learning c++ right away.


    very often when someone says:
    "why learn something new? i can do the same when sticking to the old ways!"
    or
    "why would i want that?"

    that person hasent understood the concept of the "new" thingy.

    so im not saying: don't use c in general.
    but especially many beginners use c for projects, where c++ would provide many advantages.

    im saying: *know* what you want to do and also *know* different ways of archiving that goal.
    and *then* decide which way is appropriate.
    signature under construction

  7. #22
    Registered User
    Join Date
    Dec 2005
    Posts
    15
    One thing that I see repeated that I should correct:

    I am neither 'extending' C, nor writing a new language. I'm only using what C provides.

    The structure/class definition does one thing that C++ doesn't, and that's propagate certain kinds of code into the various implementations required for a native solution to a fundamental problem - serialization. The nearest approximation to this code in C++ would essentially require you to write a nice, slow interpreter.

    One of the major reasons people turn away from C, even C++ is that these language constructs that I can now maintain trivially are such a hassle to maintain in C/C++, yet easy to manage in other languages. Additional reasons are people have awful habits with pointers, and write bad code.

    Here's one more little construct I'll share, it's called a 'generator', and it's a very old device, but it replaces the need for most kinds of threads with a very simple cooperative model. Very handy for console/embedded things.

    What it implements is a state machine with anonymous and signalled states, based on __LINE__ directives and a switch, rather than a stack and CPU/OS state. It's very handy for controlling things that happen over time, like a login protocol, or animating something while waiting for it to move. You write simple code like the following, and it will idle or wait. More complex implementations can easily be done where one expects many thousands of these things to operate, where the 'wait' states take the generator out of a list that would otherwise cycle things... but the implementation given was made for a few top-level items.

    Keep the number of conditions relatively small, and the switch overhead is insignificant. Keep the number of objects small and top-level, and even long switches are insignificant.

    Code:
    void MyFunc( MyClass* class )
    {
       {
          // Happens each cycle
       }
       gen_begin( &class->gen )
       {
          MyClass_init( class );
       }
       gen_wait( &class->gen, MyClass_acked(class) )
       {
          MyClass_localinit( class );
       }
       gen_setloop(&class->gen)
       {
          MyClass_cycle( class );
       }
       gen_handle( &class->gen, sig_Collision )
       {
          // Handle event...
          gen_loop(&class->gen);
       }
       gen_handle( &class->gen, sig_Hear )
       {
          // Handle event...
          gen_loop(&class->gen);
       }
       gen_handle( &class->gen, sig_See )
       {
          // Handle event...
          gen_loop(&class->gen);
       }
       gen_end( &class->gen, throwassert() )
       {
          // Happens once when generator 'ends'
       }
    }

    Code:
    #define _gen_begin			(1+type_min(int32))	/* Initial 'begin' state of generator, generally at start of gen handling */
    #define _gen_end			2+type_min(int32)	/* Final state of generator  Just don't define some of it in a macro on the first line of the file. */
    #define _line				-__LINE__			/* Anonymous state of generator (negative line number in source file) */
    #define _gen_signal(sig)	(sig)				/* Enumerated state of generator */
    
    
    /** 
     * \brief C++ version of generator
     *
     * Inherit from gen (class MyClass : public gen) and place handler in a member function with access to it.
     *
    **/
    #ifdef __cplusplus
    class generator
    {
    public:
    	int line;	/* Line number (if <0), or signal (if >= 0) */
    	int loop;	/* State loop point */
    	
    
    	void init()					{ line = loop = _gen_begin; }
    	void denit()				{ line = loop = _gen_end; }
    		generator()				{ init(); }
    		~generator()			{ denit(); }
    	void signal( int signal )	{ line = _gen_signal(signal); }
    };
    #else
    typedef struct generator
    {
    	int line;	/* Line number (if <0), or signal (if >= 0) */
    	int loop;	/* State loop point */
    } generator;
    #endif
    
    
    /**
     * \brief Declare a generator as static or global data
     * May appear in global scope or in structure body
     *
     * \param gen What to call it; in other invocations it should have whatever indirection is required to reach it; i.e. struct->gen
     * 
     * Works a bit like the list templates in ll.h
    **/
    #define gen_auto( gen )	gen gen = { _gen_begin, _gen_begin };\
    
    /**
     * \brief Initialize or reset a generator
     *
     * \param gen What to call it; in other invocations it should have whatever indirection is required to reach it; i.e. struct->gen
     * 
     * May appear internal or external to state function, and be conditional
    **/
    #define gen_init( gen )			{ gen.line = gen.loop = _gen_begin; }
    
    /**
     * \brief Finish a generator off
     *
     * \param gen What to call it; in other invocations it should have whatever indirection is required to reach it; i.e. struct->gen
     * 
     * May appear internal or external to state function, and be conditional
    **/
    #define gen_denit( gen )		{ gen.line = gen.loop = _gen_end; }
    
    /**
     * \brief Wake up generator, if it was 'finished', and jump to a signal for its next cycle
     *
     * May appear internal or external to state function, and be conditional
     *
     * Keep in mind that if the signal was not defined, we'll invoke the 'onbadstate' in gen_end later over it
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
     * \param signal The enumerated signal to set off when the generator wakes up
    **/
    #define gen_signal( gen, signal )	{ gen.line = _gen_signal(signal); }
    
    
    /**
     * \brief The beginning of a generator block in cycle
     *
     * It should have one or more yield states, and be terminated with an 'gen_end()'
     * Entire state machine may be wrapped by a conditional
     * Code preceding this statement will be executed each time any state is executed
     * Implementation of a state machine in the cycler is optional
     * 
     * May only appear at beginning of state machine
     * 
     * \param gen Name of generator with whatever indirection is required to reach its instance.
     * If it's a global or auto, just the 'gen' from 
    **/
    #define gen_begin( gen )\
    	switch( gen.line ) \
    	{ \
    		case _gen_begin:
    
    /**
     * \brief This is the end state, matching the gen_begin()
     * \param onbadstate Something to do when cued to unknown state; may do nothing, 
     * throw an assert on debug, a macro that harvests local state and reports, 
     * a 'catch all' subroutine handler for certain kinds of signal handlers, etc.
     *
     * May only appear at end of state machine
     *
     * Can not be part of a conditional. 
     *
     * On bad state, finishes the generator, by default
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_end( gen, onbadstate ) \
    		{} \
    		gen.line = _gen_end;\
    		/* Fall-through */\
    	case _gen_end:\
    		/* Code following state machine is executed */\
    		break;\
    	default: \
    		{\
    			/* onbadstate may optinally branch with gen_signal or gen_restart to recover harmlessly */\
    			onbadstate; \
    			gen.line = _gen_end;\
    		}\
    		return; \
    	}
    
    /**
     * \brief This will yield to main loop and start on the next piece of state when we get back.  
     *
     * May only appear in generator body between gen_begin and gen_end
     *
     * Can not be part of a conditional. 
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_yield(gen)\
    	{} \
    		gen.line = _line; \
    		return; \
    	case _line:
    
    /**
     * \brief This will yield and repeat the present state from the beginning
     *
     * May only appear in generator body between gen_begin and gen_end
     *
     * May be part of a conditional. 
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_repeat(gen)	return
    
    /**
     * \brief This will poll and not proceed to next state until 'condition' returns non-zero
     *
     * May only appear in generator body between gen_begin and gen_end
     *
     * Can not be part of a conditional. 
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
     * \param condition Condition to test for.  Flow continues when it returns true.
    **/
    #define gen_wait(gen,condition) \
    	gen_yield(gen)\
    	{ if( !(condition) ) return; }
    
    /**
     * \brief This will sit and wait until some signal occurs to wake it.
     *
     * May only appear in generator body between gen_begin and gen_end
     *
     * Can not be part of a conditional. 
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_sleep(gen) \
    	gen_yield(gen)\
    	return;
    
    /**
     * \brief This set a loop point for loop to reach
     *
     * May only appear in generator body between gen_begin and gen_end
     *
     * Can not be part of a conditional. 
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_setloop(gen) \
    	{} \
    		gen.loop = gen.line = _line; \
    		return; \
    	case _line:
    
    /**
     * \brief This will loop back to last executed gen_setloop position
     *
     * May only appear in generator body between gen_begin and gen_end
     *
     * May be part of a conditional. 
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_loop(gen) \
    	{\
    		gen.line = gen.loop; \
    		return; \
    	}
    
    /**
     * \brief This is state to accept a signal
     * \param signal The enumerated signal this handles
     * May only appear in generator body between gen_begin and gen_end
     * If state falls through to this, it will through to the end (generator is skipped in cycle until state is set)
     *
     * May only appear in generator body between gen_begin and gen_end
     *
     * Can not be part of a conditional. 
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_handle( gen, signal ) \
    	gen_sleep(gen)\
    	case _gen_signal(signal):
    
    /**
     * \brief Signal to a given state, and yield for that state to take place
     *
     * May only appear in generator body between gen_begin and gen_end
     * 
     * May be part of a conditional
     *
     * Keep in mind that if the signal was not defined, we'll invoke the 'onbadstate' in gen_end later over it
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
     * \param signal The enumerated signal to set off when the generator wakes up
    **/
    #define gen_signal_yield( gen, signal )\
    	{\
    		gen.line = _gen_signal(signal);\
    		return;\
    	}
    
    /** 
     * \brief Restart generator from beginning, and end this state
     *
     * May only appear in generator body between gen_begin and gen_end
     * 
     * May be part of a conditional
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_restart_yield(gen) \
    	{\
    		gen.line = _gen_begin;\
    		return;\
    	}
    
    /** 
     * \brief End generator, and end this state
     *
     * May only appear in generator body between gen_begin and gen_end
     * 
     * May be part of a conditional
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_finish_yield(gen) \
    	{\
    		gen.line = _gen_end;\
    		return;\
    	}
    
    /** 
     * \brief Restart generator from beginning
     *
     * May appear internal or external to state function
     * 
     * May be part of a conditional
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_restart(gen) \
    	{\
    		gen.line = _gen_begin;\
    	}
    
    /** 
     * \brief End generator 
     *
     * May appear internal or external to state function
     * 
     * May be part of a conditional
     *
     * \param gen Name of generator with whatever indirection is required to reach its instance.
    **/
    #define gen_finish(gen) \
    	{\
    		gen.line = _gen_end;\
    	}
    
    
    
    #endif /* GEN_H */

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Grouped CPP files
    By bobbelPoP in forum C++ Programming
    Replies: 12
    Last Post: 07-16-2008, 01:48 AM
  2. includein cpp file in a cpp file
    By sawer in forum C++ Programming
    Replies: 7
    Last Post: 06-15-2006, 12:34 PM
  3. Replies: 2
    Last Post: 04-09-2006, 07:20 PM
  4. Multiple Cpp Files
    By w4ck0z in forum C++ Programming
    Replies: 5
    Last Post: 11-14-2005, 02:41 PM
  5. i do i get started on cpp file
    By jlmac2001 in forum C++ Programming
    Replies: 5
    Last Post: 02-24-2003, 08:55 PM