In assembly I would

ProcA  enter 16, 0
          call    ProcB
          leave
          ret


ProcB  enter 16, 1
          ... code
          leave
          ret

Even if ProcA is in another file, so long as I know the order of the variables in ProcA they are visible to me in ProcB because 16 ,1 nests procedures frames one level.

So firstly, is there a pragma in VC++ or later versions that can force compiler to use ENTER instead of

push    ebp
mov    ebp, esp
add     esp, -16

and make one procedures local variables aware of another's without coercing them into globals

Recommended Answers

All 4 Replies

dynamic scoping is almost never used these days. with dynamic scoping, a variable reference is bound to the most recent instance of that variable on the call stack. for example:

#ifdef this_should_not_compile
void b() ; void c() ;
void a()
{
  int x = 24 ;
  int y = 32 ;
  int z = 0 ;
  b() ;
  assert( z == 132 ) ;
}

void b()
{
  int x = 100 ;
  c() ;
}

void c()
{
   z = x + y ; // would mean a()::z = b()::x + a()::y   
}

#endif // this_should_not_compile

// most C/C++ implementations use "context parameters" to simulate this
// for example, these are some Xlib functions:
//  
// int XDrawLine( Display* display, Drawable d, GC gc,
//                int x1, int y1, int x2, int y2 ) ;
// int XDrawArc( Display* display, Drawable d, GC gc,
//               unsigned int width, unsigned int height,
//               int angle1, int angle2 ) ;
//
// the parameters Display*, Drawable ang GC are the "context parameters"
// 
// with dynamic scoping, we could simply write
//
// int XDrawLine( int x1, int y1, int x2, int y2 ) ;
// int XDrawArc( unsigned int width, unsigned int height,
//               int angle1, int angle2 ) ;
//
// and the functions could pick up the caller's Display* display, 
// Drawable d, and GC gc from the stack frame

there are reasons why dynamic scoping has mostly disappeared from programming languages.
1. though it makes the code simpler when we have to pass the same arguments over and over, it produces hard to understand and hard to debug code if you have to pass different values
2. it is less efficient; binding to variables at run time vs binding at compile time
3. concurrency can either be not supported at all or can only be supported at extravagent cost (using tls to provide bindings and synchronizing bindings).

for a portable implementation of dynamic scoping in c++, see Todd Veldhuizen's treatment of dynamic scoping in C++ in his paper Techniques for Scientic C++. ftp://ftp.cs.indiana.edu/pub/techreports/TR542.pdf

a more modern implementation of dynamic scoping (also based on Veldhuizen's ideas) is available in boost.spirit. see: http://www.boost.org/libs/spirit/index.html and http://www.boost.org/libs/spirit/phoenix/index.html in spirit, closures provide an environment (a stack frame) for local variables. closure variables can be used to pass information up or down the call stack. here is a very simple example:

#include <boost/spirit/core.hpp>
#include <boost/spirit/attribute.hpp>
#include <iostream>
#include <string>
using namespace std ;
using namespace boost::spirit ;
using namespace phoenix ;

const actor< argument<0> > number = argument<0>()  ; // closure binding

struct closure_t : public boost::spirit::closure< closure_t, double >
{ member1 value ; } ;

struct calculator : public grammar< calculator, closure_t::context_t >
{
    template < typename scanner_type > struct definition
    {
        // closure_t context propagates the expression result (value) upwards
        rule< scanner_type, closure_t::context_t > expression, term, factor ;
        rule<scanner_type> top_rule ;

        const rule<scanner_type>& start() const { return top_rule ; }

        explicit definition( const calculator& calc ) // the grammar
        {
            top_rule = expression[ calc.value = number ] ;

            expression = term [ expression.value = number ]
                 >> *  (   ( '+' >> term [ expression.value += number ] )
                      | ( '-' >> term [ expression.value -= number ] ) ) ;

            term = factor[ term.value = number ]
                     >> * (    ( '*' >> factor[ term.value *= number ] )
                            |  ( '/' >> factor[ term.value /= number ] ) ) ;

            factor = ureal_p[ factor.value = number ]
                     |   '(' >> expression[ factor.value = number ] >> ')'
                     |   ( '-' >> factor[ factor.value = -number ] )
                     |   ( '+' >> factor[ factor.value = number ] ) ;
        }
    };
};

int main()
{
  cout << "basic calculator operators: (, ), +, -. *, / numbers: 2, 3.56, 1.3e-2 etc. \n" ;
  const calculator calculator ;  // parser
  double result = 0.0 ;
  string str ;
  while( cout << ">> " && getline(cin, str) && !str.empty() 
             && cout << str << " = " )
  {
    parse_info<> info = parse( str.c_str(),
                      calculator[ var( result ) = number ],
                      space_p ) ; // skip white spaces between tokens
    if( info.full ) cout << result << "\n" ;
    else cout << "> parse error; stop at: \"" << info.stop << "\"\n" ;
  }
}

/**
 calc_testcases.txt
 ------------------
 2.3 * ( 3.2+e2 - 24.3 )
 3-( 5e0 * (7.2+1.1e1) ) - ( 2.4e-3* -5.8 + +1.3 )
 3.2 / ( 6.34 - +(1.7) )
 2.3 * + ( 3.2+e2 - * 24.3 )
 3-( 5e0 * (7.2+1.1e1) ) - ( 2.4e-3* -5.8 + (1.3 )
 3.2 / ( 6.34 - +(1.7) ) )
*/

/**
 output:
> g++ -Wall -std=c++98 -I/usr/local/include calc.cpp
> ./a.out < calc_testcases.txt
basic calculator operators: (), +, -. *, / numbers: 2, 3.56, 1.3e-2 etc.
>>  2.3  * + ( +3.2e2 - 24.3 ) = 680.11
>>  3-( 5e0 * (7.2+1.1e1) ) - ( 2.4e-3* -5.8 + +1.3 ) = -89.2861
>>  3.2 / ( 6.34 - +(1.7) ) = 0.689655
>>  2.3 * + ( +3.2+e2 - * 24.3 ) = > parse error; stop at: " * + ( +3.2+e2 - * 24.3 )"
>>  3-( 5e0 * (7.2+1.1e1) ) - ( 2.4e-3* -5.8 + (1.3 ) = > parse error; stop at: " - ( 2.4e-3* -5.8 + (1.3 )"
>>  3.2 / ( 6.34 - +(1.7) ) ) = > parse error; stop at: " )"
*/

another C++ library with support for dynamic scoping is FC++. see: http://www.cc.gatech.edu/~yannis/fc++/

commented: Ah yes, closures - a lot easier in some other languages. +9

Thank-you, this has definitely opened the eyes amongst our development group that assembly is much more efficient for our intended project.

Why do I get the feeling that this is just another case of rampant premature optimisation?

So you're going to throw away all the advantages of C++, say faster development time, fewer bugs, easier to fix bugs, benefits of program-wide optimisations provided by the compiler, better tools (etc etc) in favour of writing in ASM just to get at one small trick?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.