Monday, 24 March 2008

A lame implementation of dynamic scoping in C++

This is very lame. I have nothing to say for myself except I'm very sorry.

#include <vector>
#include <iostream>
#include <boost/thread/tss.hpp>

#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/thread/barrier.hpp>

/**
Dynamic scoping is a form of scoping where the bindings associated
with variables are determined at runtime.

C++ has lexical scoping which is a form of scoping where the
bindings associated with are known at compile-time.

This piece of code is a half-assed implementation of thread-safe
dynamic variables in C++. See the main function in this file for a
"test" of the thread-safety

If I cared enough, I would make the variables get the global
bindings when new threads were created but as of now, they only get
the default value.

Alas, I don't care enough.

Usage:

// at global scope
dynamic_variable<int> dv_foo(5); // default value
dynamic_variable<double> dv_bar; // defaults to 0

void print()
{
std::cout << "FOO: " << dv_foo() << " BAR: " << dv_bar() << std::endl;
}

int main(int argc, char * argv[])
{
{
dynamic_bind<int> foo(dv_foo,10);
dynamic_bind<double> bar(dv_bar,10);
print();
}
{
dynamic_bind<int> foo(dv_foo,20);
dynamic_bind<double> bar(dv_bar,20);
print();
}
print();
}

output:
FOO: 10 BAR: 10
FOO: 20 BAR: 20
FOO: 5 BAR: 0
**/


template
<typename T> struct dynamic_bind;

template
<typename T>

struct
dynamic_variable
{

dynamic_variable(T const & t)
:
initial_value_(t)
{


init();
}

dynamic_variable()
:
initial_value_(T())
{

init();
}


T & operator()()
{

init();
return
bindings_.get()->back();
}


private
:
friend class
dynamic_bind<T>;

void
init()
{


if
(!bindings_.get())
{

bindings_.reset(new std::vector<T>());

bind(initial_value_);
}
}


void
bind(T const & t)
{


init();
bindings_.get()->push_back(t);
}

void
unbind()
{


init();
bindings_.get()->pop_back();
}


boost::thread_specific_ptr<std::vector<T> > bindings_;

T initial_value_;
};


template
<typename T>
struct
dynamic_bind
{

dynamic_bind(dynamic_variable<T> & var,

T const & t):
var_(var)
{

var_.bind(t);
}
~
dynamic_bind()
{


var_.unbind();
}

private
:
dynamic_variable<T> & var_;
};



dynamic_variable<int> dv_thread_id(0);
dynamic_variable<std::string> dv_x;

dynamic_variable<std::string> dv_y("global y");

void
reader()
{

std::cout << "In thread " << dv_thread_id()
<<
", x = " << dv_x()
<<
", y = " << dv_y()
<<
std::endl;
}


void
setter()
{

boost::barrier wait_for(2);

dynamic_bind<std::string> x(dv_x,"setter::x1");
{


// yield to allow for interleaving of threads
boost::thread::yield();
dynamic_bind<std::string> x(dv_x,"setter::x2");

// first time this function is called, y has not
// been dynamically rebound, so y will have its
// default value
reader();
}

dynamic_bind<std::string> y(dv_y,"setter::y1");
{


boost::thread::yield();
reader();
}
}


void
threadfunc(int threadid)
{


dynamic_bind<int> thread_id(dv_thread_id,threadid);
setter();
}


int
main()
{


std::cout << "In main..." << std::endl;
boost::thread t1(boost::bind(threadfunc,1));

boost::thread t2(boost::bind(threadfunc,2));
t1.join();

t2.join();
}


No comments: