<commit_before>#include "vm.hpp"
#include "state.hpp"
#include "shared_state.hpp"
#include "call_frame.hpp"
#include "config_parser.hpp"
#include "config.h"
#include "memory.hpp"
#include "environment.hpp"
#include "instruments/tooling.hpp"
#include "instruments/timing.hpp"
#include "global_cache.hpp"

#include "util/thread.hpp"
#include "configuration.hpp"

#include "console.hpp"
#include "metrics.hpp"
#include "signal.hpp"
#include "builtin/randomizer.hpp"
#include "builtin/array.hpp"
#include "builtin/thread.hpp"
#include "builtin/native_method.hpp"

#include <iostream>
#include <iomanip>

#ifdef ENABLE_LLVM
#include "jit/llvm/state.hpp"
#endif

namespace rubinius {

  SharedState::SharedState(Environment* env, Configuration& config, ConfigParser& cp)
    : thread_nexus_(new ThreadNexus())
    , internal_threads_(0)
    , signals_(0)
    , finalizer_thread_(0)
    , console_(0)
    , metrics_(0)
    , method_count_(1)
    , class_count_(1)
    , global_serial_(1)
    , initialized_(false)
    , check_global_interrupts_(false)
    , check_gc_(false)
    , root_vm_(0)
    , env_(env)
    , tool_broker_(new tooling::ToolBroker)
    , codedb_lock_(true)
    , capi_ds_lock_()
    , capi_locks_lock_()
    , capi_constant_lock_()
    , global_capi_handle_lock_()
    , capi_handle_cache_lock_()
    , llvm_state_lock_()
    , wait_lock_()
    , type_info_lock_()
    , code_resource_lock_()
    , use_capi_lock_(false)
    , om(0)
    , global_cache(new GlobalCache)
    , config(config)
    , user_variables(cp)
    , llvm_state(0)
    , username("")
    , pid("")
  {
    internal_threads_ = new InternalThreads();

    for(int i = 0; i < Primitives::cTotalPrimitives; i++) {
      primitive_hits_[i] = 0;
    }

    hash_seed = Randomizer::random_uint32();
    initialize_capi_black_list();
  }

  SharedState::~SharedState() {
    if(!initialized_) return;

#ifdef ENABLE_LLVM
    if(llvm_state) {
      delete llvm_state;
    }
#endif

    if(console_) {
      delete console_;
      console_ = 0;
    }

    if(metrics_) {
      delete metrics_;
      metrics_ = 0;
    }

    delete tool_broker_;
    delete global_cache;
    delete om;
    delete internal_threads_;
  }

  Array* SharedState::vm_threads(STATE) {
    utilities::thread::SpinLock::LockGuard guard(thread_nexus_->threads_lock());

    Array* threads = Array::create(state, 0);

    for(ThreadList::iterator i = thread_nexus_->threads()->begin();
        i != thread_nexus_->threads()->end();
        ++i)
    {
      if(VM* vm = (*i)->as_vm()) {
        Thread *thread = vm->thread.get();
        if(!thread->nil_p() && CBOOL(thread->alive())) {
          threads->append(state, thread);
        }
      }
    }

    return threads;
  }

  SignalThread* SharedState::start_signals(STATE) {
    signals_ = new SignalThread(state, state->vm());
    signals_->start(state);

    return signals_;
  }

  console::Console* SharedState::start_console(STATE) {
    if(!console_) {
      console_ = new console::Console(state);
      console_->start(state);
    }

    return console_;
  }

  metrics::Metrics* SharedState::start_metrics(STATE) {
    if(!metrics_) {
      metrics_ = new metrics::Metrics(state);
      metrics_->start(state);
      metrics_->init_ruby_metrics(state);
    }

    return metrics_;
  }

  void SharedState::disable_metrics(STATE) {
    if(metrics_) metrics_->disable(state);
  }

  void SharedState::after_fork_child(STATE) {
    // For now, we disable inline debugging here. This makes inspecting
    // it much less confusing.
    config.jit_inline_debug.set("no");

    disable_metrics(state);

    // Reinit the locks for this object
    global_cache->reset();
    codedb_lock_.init(true);
    capi_ds_lock_.init();
    capi_locks_lock_.init();
    capi_constant_lock_.init();
    global_capi_handle_lock_.init();
    capi_handle_cache_lock_.init();
    llvm_state_lock_.init();
    wait_lock_.init();
    type_info_lock_.init();
    code_resource_lock_.init();

    om->after_fork_child(state);
    signals_->after_fork_child(state);
    console_->after_fork_child(state);
  }

  const unsigned int* SharedState::object_memory_mark_address() const {
    return om->mark_address();
  }

  void SharedState::enter_capi(STATE, const char* file, int line) {
    NativeMethodEnvironment* env = state->vm()->native_method_environment;
    if(int lock_index = env->current_native_frame()->capi_lock_index()) {
      capi_locks_[lock_index - 1]->lock();
    }
  }

  void SharedState::leave_capi(STATE) {
    NativeMethodEnvironment* env = state->vm()->native_method_environment;
    if(int lock_index = env->current_native_frame()->capi_lock_index()) {
      capi_locks_[lock_index - 1]->unlock();
    }
  }

  int SharedState::capi_lock_index(std::string name) {
    utilities::thread::SpinLock::LockGuard guard(capi_locks_lock_);
    int existing = capi_lock_map_[name];
    if(existing) return existing;

    CApiBlackList::const_iterator blacklisted = capi_black_list_.find(name);

    // Only disable locks if we have capi locks disabled
    // and library is not in the blacklist.
    if(!use_capi_lock_ && blacklisted == capi_black_list_.end()) {
      capi_lock_map_[name] = 0;
      return 0;
    }

    utilities::thread::Mutex* lock = new utilities::thread::Mutex(true);
    capi_locks_.push_back(lock);

    // We use a 1 offset index, so 0 can indicate no lock used
    int lock_index = capi_locks_.size();
    capi_lock_map_[name] = lock_index;
    return lock_index;
  }

  void SharedState::setup_capi_constant_names() {
    capi_constant_name_map_.resize(cCApiMaxConstant + 1);

    capi_constant_name_map_[cCApiArray]      = "Array";
    capi_constant_name_map_[cCApiBignum]     = "Bignum";
    capi_constant_name_map_[cCApiClass]      = "Class";
    capi_constant_name_map_[cCApiComparable] = "Comparable";
    capi_constant_name_map_[cCApiData]       = "Data";
    capi_constant_name_map_[cCApiEnumerable] = "Enumerable";
    capi_constant_name_map_[cCApiFalse]      = "FalseClass";
    capi_constant_name_map_[cCApiFile]       = "File";
    capi_constant_name_map_[cCApiFixnum]     = "Fixnum";
    capi_constant_name_map_[cCApiFloat]      = "Float";
    capi_constant_name_map_[cCApiHash]       = "Hash";
    capi_constant_name_map_[cCApiInteger]    = "Integer";
    capi_constant_name_map_[cCApiIO]         = "IO";
    capi_constant_name_map_[cCApiKernel]     = "Kernel";
    capi_constant_name_map_[cCApiMatch]      = "MatchData";
    capi_constant_name_map_[cCApiModule]     = "Module";
    capi_constant_name_map_[cCApiNil]        = "NilClass";
    capi_constant_name_map_[cCApiNumeric]    = "Numeric";
    capi_constant_name_map_[cCApiObject]     = "Object";
    capi_constant_name_map_[cCApiRange]      = "Range";
    capi_constant_name_map_[cCApiRegexp]     = "Regexp";
    capi_constant_name_map_[cCApiRubinius]   = "Rubinius";
    capi_constant_name_map_[cCApiString]     = "String";
    capi_constant_name_map_[cCApiStruct]     = "Struct";
    capi_constant_name_map_[cCApiSymbol]     = "Symbol";
    capi_constant_name_map_[cCApiThread]     = "Thread";
    capi_constant_name_map_[cCApiTime]       = "Time";
    capi_constant_name_map_[cCApiTrue]       = "TrueClass";
    capi_constant_name_map_[cCApiProc]       = "Proc";
    capi_constant_name_map_[cCApiGC]         = "GC";
    capi_constant_name_map_[cCApiCAPI]       = "Rubinius::CAPI";
    capi_constant_name_map_[cCApiMethod]     = "Method";
    capi_constant_name_map_[cCApiRational]   = "Rational";
    capi_constant_name_map_[cCApiComplex]    = "Complex";
    capi_constant_name_map_[cCApiEnumerator] = "Enumerable::Enumerator";
    capi_constant_name_map_[cCApiMutex]      = "Mutex";
    capi_constant_name_map_[cCApiDir]        = "Dir";

    capi_constant_name_map_[cCApiArgumentError]       = "ArgumentError";
    capi_constant_name_map_[cCApiEOFError]            = "EOFError";
    capi_constant_name_map_[cCApiErrno]               = "Errno";
    capi_constant_name_map_[cCApiException]           = "Exception";
    capi_constant_name_map_[cCApiFatal]               = "FatalError";
    capi_constant_name_map_[cCApiFloatDomainError]    = "FloatDomainError";
    capi_constant_name_map_[cCApiIndexError]          = "IndexError";
    capi_constant_name_map_[cCApiInterrupt]           = "Interrupt";
    capi_constant_name_map_[cCApiIOError]             = "IOError";
    capi_constant_name_map_[cCApiLoadError]           = "LoadError";
    capi_constant_name_map_[cCApiLocalJumpError]      = "LocalJumpError";
    capi_constant_name_map_[cCApiNameError]           = "NameError";
    capi_constant_name_map_[cCApiNoMemoryError]       = "NoMemoryError";
    capi_constant_name_map_[cCApiNoMethodError]       = "NoMethodError";
    capi_constant_name_map_[cCApiNotImplementedError] = "NotImplementedError";
    capi_constant_name_map_[cCApiRangeError]          = "RangeError";
    capi_constant_name_map_[cCApiRegexpError]         = "RegexpError";
    capi_constant_name_map_[cCApiRuntimeError]        = "RuntimeError";
    capi_constant_name_map_[cCApiScriptError]         = "ScriptError";
    capi_constant_name_map_[cCApiSecurityError]       = "SecurityError";
    capi_constant_name_map_[cCApiSignalException]     = "SignalException";
    capi_constant_name_map_[cCApiStandardError]       = "StandardError";
    capi_constant_name_map_[cCApiSyntaxError]         = "SyntaxError";
    capi_constant_name_map_[cCApiSystemCallError]     = "SystemCallError";
    capi_constant_name_map_[cCApiSystemExit]          = "SystemExit";
    capi_constant_name_map_[cCApiSystemStackError]    = "SystemStackError";
    capi_constant_name_map_[cCApiTypeError]           = "TypeError";
    capi_constant_name_map_[cCApiThreadError]         = "ThreadError";
    capi_constant_name_map_[cCApiZeroDivisionError]   = "ZeroDivisionError";

    capi_constant_name_map_[cCApiMathDomainError]     = "Math::DomainError";
    capi_constant_name_map_[cCApiEncoding]            = "Encoding";
    capi_constant_name_map_[cCApiEncCompatError]      = "Encoding::CompatibilityError";
    capi_constant_name_map_[cCApiWaitReadable]        = "IO::WaitReadable";
    capi_constant_name_map_[cCApiWaitWritable]        = "IO::WaitWritable";

  }

#define CAPI_BLACK_LIST(name) capi_black_list_.insert(std::string("Init_" # name))
  void SharedState::initialize_capi_black_list() {
    CAPI_BLACK_LIST(nkf);
    CAPI_BLACK_LIST(nokogiri);
  }
}
<commit_msg>Don't start Metrics if not requested.<commit_after>#include "vm.hpp"
#include "state.hpp"
#include "shared_state.hpp"
#include "call_frame.hpp"
#include "config_parser.hpp"
#include "config.h"
#include "memory.hpp"
#include "environment.hpp"
#include "instruments/tooling.hpp"
#include "instruments/timing.hpp"
#include "global_cache.hpp"

#include "util/thread.hpp"
#include "configuration.hpp"

#include "console.hpp"
#include "metrics.hpp"
#include "signal.hpp"
#include "builtin/randomizer.hpp"
#include "builtin/array.hpp"
#include "builtin/thread.hpp"
#include "builtin/native_method.hpp"

#include <iostream>
#include <iomanip>

#ifdef ENABLE_LLVM
#include "jit/llvm/state.hpp"
#endif

namespace rubinius {

  SharedState::SharedState(Environment* env, Configuration& config, ConfigParser& cp)
    : thread_nexus_(new ThreadNexus())
    , internal_threads_(0)
    , signals_(0)
    , finalizer_thread_(0)
    , console_(0)
    , metrics_(0)
    , method_count_(1)
    , class_count_(1)
    , global_serial_(1)
    , initialized_(false)
    , check_global_interrupts_(false)
    , check_gc_(false)
    , root_vm_(0)
    , env_(env)
    , tool_broker_(new tooling::ToolBroker)
    , codedb_lock_(true)
    , capi_ds_lock_()
    , capi_locks_lock_()
    , capi_constant_lock_()
    , global_capi_handle_lock_()
    , capi_handle_cache_lock_()
    , llvm_state_lock_()
    , wait_lock_()
    , type_info_lock_()
    , code_resource_lock_()
    , use_capi_lock_(false)
    , om(0)
    , global_cache(new GlobalCache)
    , config(config)
    , user_variables(cp)
    , llvm_state(0)
    , username("")
    , pid("")
  {
    internal_threads_ = new InternalThreads();

    for(int i = 0; i < Primitives::cTotalPrimitives; i++) {
      primitive_hits_[i] = 0;
    }

    hash_seed = Randomizer::random_uint32();
    initialize_capi_black_list();
  }

  SharedState::~SharedState() {
    if(!initialized_) return;

#ifdef ENABLE_LLVM
    if(llvm_state) {
      delete llvm_state;
    }
#endif

    if(console_) {
      delete console_;
      console_ = 0;
    }

    if(metrics_) {
      delete metrics_;
      metrics_ = 0;
    }

    delete tool_broker_;
    delete global_cache;
    delete om;
    delete internal_threads_;
  }

  Array* SharedState::vm_threads(STATE) {
    utilities::thread::SpinLock::LockGuard guard(thread_nexus_->threads_lock());

    Array* threads = Array::create(state, 0);

    for(ThreadList::iterator i = thread_nexus_->threads()->begin();
        i != thread_nexus_->threads()->end();
        ++i)
    {
      if(VM* vm = (*i)->as_vm()) {
        Thread *thread = vm->thread.get();
        if(!thread->nil_p() && CBOOL(thread->alive())) {
          threads->append(state, thread);
        }
      }
    }

    return threads;
  }

  SignalThread* SharedState::start_signals(STATE) {
    signals_ = new SignalThread(state, state->vm());
    signals_->start(state);

    return signals_;
  }

  console::Console* SharedState::start_console(STATE) {
    if(!console_) {
      console_ = new console::Console(state);
      console_->start(state);
    }

    return console_;
  }

  metrics::Metrics* SharedState::start_metrics(STATE) {
    if(state->shared().config.system_metrics_target.value.compare("none")) {
      if(!metrics_) {
        metrics_ = new metrics::Metrics(state);
        metrics_->start(state);
        metrics_->init_ruby_metrics(state);
      }
    }

    return metrics_;
  }

  void SharedState::disable_metrics(STATE) {
    if(metrics_) metrics_->disable(state);
  }

  void SharedState::after_fork_child(STATE) {
    // For now, we disable inline debugging here. This makes inspecting
    // it much less confusing.
    config.jit_inline_debug.set("no");

    disable_metrics(state);

    // Reinit the locks for this object
    global_cache->reset();
    codedb_lock_.init(true);
    capi_ds_lock_.init();
    capi_locks_lock_.init();
    capi_constant_lock_.init();
    global_capi_handle_lock_.init();
    capi_handle_cache_lock_.init();
    llvm_state_lock_.init();
    wait_lock_.init();
    type_info_lock_.init();
    code_resource_lock_.init();

    om->after_fork_child(state);
    signals_->after_fork_child(state);
    console_->after_fork_child(state);
  }

  const unsigned int* SharedState::object_memory_mark_address() const {
    return om->mark_address();
  }

  void SharedState::enter_capi(STATE, const char* file, int line) {
    NativeMethodEnvironment* env = state->vm()->native_method_environment;
    if(int lock_index = env->current_native_frame()->capi_lock_index()) {
      capi_locks_[lock_index - 1]->lock();
    }
  }

  void SharedState::leave_capi(STATE) {
    NativeMethodEnvironment* env = state->vm()->native_method_environment;
    if(int lock_index = env->current_native_frame()->capi_lock_index()) {
      capi_locks_[lock_index - 1]->unlock();
    }
  }

  int SharedState::capi_lock_index(std::string name) {
    utilities::thread::SpinLock::LockGuard guard(capi_locks_lock_);
    int existing = capi_lock_map_[name];
    if(existing) return existing;

    CApiBlackList::const_iterator blacklisted = capi_black_list_.find(name);

    // Only disable locks if we have capi locks disabled
    // and library is not in the blacklist.
    if(!use_capi_lock_ && blacklisted == capi_black_list_.end()) {
      capi_lock_map_[name] = 0;
      return 0;
    }

    utilities::thread::Mutex* lock = new utilities::thread::Mutex(true);
    capi_locks_.push_back(lock);

    // We use a 1 offset index, so 0 can indicate no lock used
    int lock_index = capi_locks_.size();
    capi_lock_map_[name] = lock_index;
    return lock_index;
  }

  void SharedState::setup_capi_constant_names() {
    capi_constant_name_map_.resize(cCApiMaxConstant + 1);

    capi_constant_name_map_[cCApiArray]      = "Array";
    capi_constant_name_map_[cCApiBignum]     = "Bignum";
    capi_constant_name_map_[cCApiClass]      = "Class";
    capi_constant_name_map_[cCApiComparable] = "Comparable";
    capi_constant_name_map_[cCApiData]       = "Data";
    capi_constant_name_map_[cCApiEnumerable] = "Enumerable";
    capi_constant_name_map_[cCApiFalse]      = "FalseClass";
    capi_constant_name_map_[cCApiFile]       = "File";
    capi_constant_name_map_[cCApiFixnum]     = "Fixnum";
    capi_constant_name_map_[cCApiFloat]      = "Float";
    capi_constant_name_map_[cCApiHash]       = "Hash";
    capi_constant_name_map_[cCApiInteger]    = "Integer";
    capi_constant_name_map_[cCApiIO]         = "IO";
    capi_constant_name_map_[cCApiKernel]     = "Kernel";
    capi_constant_name_map_[cCApiMatch]      = "MatchData";
    capi_constant_name_map_[cCApiModule]     = "Module";
    capi_constant_name_map_[cCApiNil]        = "NilClass";
    capi_constant_name_map_[cCApiNumeric]    = "Numeric";
    capi_constant_name_map_[cCApiObject]     = "Object";
    capi_constant_name_map_[cCApiRange]      = "Range";
    capi_constant_name_map_[cCApiRegexp]     = "Regexp";
    capi_constant_name_map_[cCApiRubinius]   = "Rubinius";
    capi_constant_name_map_[cCApiString]     = "String";
    capi_constant_name_map_[cCApiStruct]     = "Struct";
    capi_constant_name_map_[cCApiSymbol]     = "Symbol";
    capi_constant_name_map_[cCApiThread]     = "Thread";
    capi_constant_name_map_[cCApiTime]       = "Time";
    capi_constant_name_map_[cCApiTrue]       = "TrueClass";
    capi_constant_name_map_[cCApiProc]       = "Proc";
    capi_constant_name_map_[cCApiGC]         = "GC";
    capi_constant_name_map_[cCApiCAPI]       = "Rubinius::CAPI";
    capi_constant_name_map_[cCApiMethod]     = "Method";
    capi_constant_name_map_[cCApiRational]   = "Rational";
    capi_constant_name_map_[cCApiComplex]    = "Complex";
    capi_constant_name_map_[cCApiEnumerator] = "Enumerable::Enumerator";
    capi_constant_name_map_[cCApiMutex]      = "Mutex";
    capi_constant_name_map_[cCApiDir]        = "Dir";

    capi_constant_name_map_[cCApiArgumentError]       = "ArgumentError";
    capi_constant_name_map_[cCApiEOFError]            = "EOFError";
    capi_constant_name_map_[cCApiErrno]               = "Errno";
    capi_constant_name_map_[cCApiException]           = "Exception";
    capi_constant_name_map_[cCApiFatal]               = "FatalError";
    capi_constant_name_map_[cCApiFloatDomainError]    = "FloatDomainError";
    capi_constant_name_map_[cCApiIndexError]          = "IndexError";
    capi_constant_name_map_[cCApiInterrupt]           = "Interrupt";
    capi_constant_name_map_[cCApiIOError]             = "IOError";
    capi_constant_name_map_[cCApiLoadError]           = "LoadError";
    capi_constant_name_map_[cCApiLocalJumpError]      = "LocalJumpError";
    capi_constant_name_map_[cCApiNameError]           = "NameError";
    capi_constant_name_map_[cCApiNoMemoryError]       = "NoMemoryError";
    capi_constant_name_map_[cCApiNoMethodError]       = "NoMethodError";
    capi_constant_name_map_[cCApiNotImplementedError] = "NotImplementedError";
    capi_constant_name_map_[cCApiRangeError]          = "RangeError";
    capi_constant_name_map_[cCApiRegexpError]         = "RegexpError";
    capi_constant_name_map_[cCApiRuntimeError]        = "RuntimeError";
    capi_constant_name_map_[cCApiScriptError]         = "ScriptError";
    capi_constant_name_map_[cCApiSecurityError]       = "SecurityError";
    capi_constant_name_map_[cCApiSignalException]     = "SignalException";
    capi_constant_name_map_[cCApiStandardError]       = "StandardError";
    capi_constant_name_map_[cCApiSyntaxError]         = "SyntaxError";
    capi_constant_name_map_[cCApiSystemCallError]     = "SystemCallError";
    capi_constant_name_map_[cCApiSystemExit]          = "SystemExit";
    capi_constant_name_map_[cCApiSystemStackError]    = "SystemStackError";
    capi_constant_name_map_[cCApiTypeError]           = "TypeError";
    capi_constant_name_map_[cCApiThreadError]         = "ThreadError";
    capi_constant_name_map_[cCApiZeroDivisionError]   = "ZeroDivisionError";

    capi_constant_name_map_[cCApiMathDomainError]     = "Math::DomainError";
    capi_constant_name_map_[cCApiEncoding]            = "Encoding";
    capi_constant_name_map_[cCApiEncCompatError]      = "Encoding::CompatibilityError";
    capi_constant_name_map_[cCApiWaitReadable]        = "IO::WaitReadable";
    capi_constant_name_map_[cCApiWaitWritable]        = "IO::WaitWritable";

  }

#define CAPI_BLACK_LIST(name) capi_black_list_.insert(std::string("Init_" # name))
  void SharedState::initialize_capi_black_list() {
    CAPI_BLACK_LIST(nkf);
    CAPI_BLACK_LIST(nokogiri);
  }
}
<|endoftext|>


<commit_before>//  (C) Copyright Gennadiy Rozental 2005-2010.
//  Use, modification, and distribution are subject to the
//  Boost Software License, ELOG_VER 1.0. (See accompanying file
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.
//
//  File        : $RCSfile$
//
//  Version     : $Revision$
//
//  Description : Facilities to perform interaction based testng of logged expectations
// ***************************************************************************

#ifndef BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER
#define BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER

// Boost.Test
#include <boost/test/detail/config.hpp>

#if BOOST_TEST_SUPPORT_INTERACTION_TESTING

#include <boost/test/detail/global_typedef.hpp>

#include <boost/test/utils/iterator/token_iterator.hpp>

#include <boost/test/interaction/interaction_based.hpp>
#include <boost/test/test_tools.hpp>

#include <boost/test/detail/suppress_warnings.hpp>

// Boost
#include <boost/lexical_cast.hpp>
#include <boost/function/function0.hpp>

// STL
#include <fstream>

//____________________________________________________________________________//

namespace boost {

using namespace ::boost::unit_test;

namespace itest {

// ************************************************************************** //
// **************    logged expectation test implementation    ************** //
// ************************************************************************** //

struct expectations_logger : itest::manager {
    // Constructor
    expectations_logger( const_string log_file_name, bool test_or_log );

    virtual bool        decision_point( const_string, std::size_t );
    virtual unsigned    enter_scope( const_string, std::size_t, const_string scope_name );
    virtual void        allocated( const_string, std::size_t, void*, std::size_t s );
    virtual void        data_flow( const_string d );
    virtual std::string return_value( const_string default_value );

private:
    // Data members
    bool            m_test_or_log;
    std::fstream    m_log_file;
};

literal_string ELOG_VER     = "1.0";
literal_string CLMN_SEP     = "|";
static const char LINE_SEP  = '\n';

literal_string FILE_SIG     = "ELOG";
literal_string SCOPE_SIG    = "SCOPE";
literal_string ALLOC_SIG    = "ALLOC";
literal_string DP_SIG       = "SWITCH";
literal_string DATA_SIG     = "DATA";
literal_string RETURN_SIG   = "RETURN";

//____________________________________________________________________________//

expectations_logger::expectations_logger( const_string log_file_name, bool test_or_log )
: m_test_or_log( test_or_log )
{
    BOOST_REQUIRE_MESSAGE( !log_file_name.is_empty(), "Empty expectations log file name" );

    m_log_file.open( log_file_name.begin(), test_or_log ? std::ios::in : std::ios::out );

    BOOST_REQUIRE_MESSAGE( m_log_file.is_open(),
                           "Can't open expectations log file " << log_file_name
                                << " for " << ( m_test_or_log ? "reading" : "writing") );

    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, FILE_SIG );
        ++tit;
        BOOST_CHECK_EQUAL( *tit, ELOG_VER );
    }
    else {
        m_log_file << FILE_SIG << CLMN_SEP << ELOG_VER << LINE_SEP;
    }
}

//____________________________________________________________________________//

bool
expectations_logger::decision_point( const_string, std::size_t )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, DP_SIG ); ++tit;
        return lexical_cast<bool>( *tit );
    }
    else {
        m_log_file << DP_SIG << CLMN_SEP << std::boolalpha << true << LINE_SEP;

        return true;
    }
}

//____________________________________________________________________________//

unsigned
expectations_logger::enter_scope( const_string, std::size_t, const_string scope_name )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, SCOPE_SIG ); ++tit;
        BOOST_CHECK_EQUAL( *tit, scope_name );
    }
    else {
        m_log_file << SCOPE_SIG << CLMN_SEP << scope_name << LINE_SEP;
    }

    return 0;
}

//____________________________________________________________________________//

void
expectations_logger::allocated( const_string, std::size_t, void*, std::size_t s )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, ALLOC_SIG ); ++tit;
        BOOST_CHECK_EQUAL( lexical_cast<std::size_t>( *tit ), s );
    }
    else {
        m_log_file << ALLOC_SIG << CLMN_SEP << s << LINE_SEP;
    }
}

//____________________________________________________________________________//

void
expectations_logger::data_flow( const_string d )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, DATA_SIG ); ++tit;
        BOOST_CHECK_EQUAL( *tit, d );
    }
    else {
        m_log_file << DATA_SIG << CLMN_SEP << d << LINE_SEP;
    }
}

//____________________________________________________________________________//

std::string
expectations_logger::return_value( const_string default_value )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, RETURN_SIG ); ++tit;

        return std::string( tit->begin(), tit->size() );
    }
    else {
        m_log_file << RETURN_SIG << CLMN_SEP << default_value << LINE_SEP;

        return std::string();
    }
}

//____________________________________________________________________________//

// ************************************************************************** //
// **************           logged expectations test           ************** //
// ************************************************************************** //

void BOOST_TEST_DECL
logged_expectations( boost::function<void ()> const& F, const_string log_file_name, bool test_or_log )
{
    expectations_logger el( log_file_name, test_or_log );

    F();
}

//____________________________________________________________________________//

}  // namespace itest
} // namespace boost

#include <boost/test/detail/enable_warnings.hpp>

#endif // not ancient compiler

#endif // BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER
<commit_msg>fixed namespace<commit_after>//  (C) Copyright Gennadiy Rozental 2005-2010.
//  Use, modification, and distribution are subject to the
//  Boost Software License, ELOG_VER 1.0. (See accompanying file
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.
//
//  File        : $RCSfile$
//
//  Version     : $Revision$
//
//  Description : Facilities to perform interaction based testng of logged expectations
// ***************************************************************************

#ifndef BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER
#define BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER

// Boost.Test
#include <boost/test/detail/config.hpp>

#if BOOST_TEST_SUPPORT_INTERACTION_TESTING

#include <boost/test/detail/global_typedef.hpp>

#include <boost/test/utils/iterator/token_iterator.hpp>

#include <boost/test/interaction/interaction_based.hpp>
#include <boost/test/test_tools.hpp>

#include <boost/test/detail/suppress_warnings.hpp>

// Boost
#include <boost/lexical_cast.hpp>
#include <boost/function/function0.hpp>

// STL
#include <fstream>

//____________________________________________________________________________//

namespace boost {

namespace itest {

using namespace ::boost::unit_test;

// ************************************************************************** //
// **************    logged expectation test implementation    ************** //
// ************************************************************************** //

struct expectations_logger : itest::manager {
    // Constructor
    expectations_logger( const_string log_file_name, bool test_or_log );

    virtual bool        decision_point( const_string, std::size_t );
    virtual unsigned    enter_scope( const_string, std::size_t, const_string scope_name );
    virtual void        allocated( const_string, std::size_t, void*, std::size_t s );
    virtual void        data_flow( const_string d );
    virtual std::string return_value( const_string default_value );

private:
    // Data members
    bool            m_test_or_log;
    std::fstream    m_log_file;
};

literal_string ELOG_VER     = "1.0";
literal_string CLMN_SEP     = "|";
static const char LINE_SEP  = '\n';

literal_string FILE_SIG     = "ELOG";
literal_string SCOPE_SIG    = "SCOPE";
literal_string ALLOC_SIG    = "ALLOC";
literal_string DP_SIG       = "SWITCH";
literal_string DATA_SIG     = "DATA";
literal_string RETURN_SIG   = "RETURN";

//____________________________________________________________________________//

expectations_logger::expectations_logger( const_string log_file_name, bool test_or_log )
: m_test_or_log( test_or_log )
{
    BOOST_REQUIRE_MESSAGE( !log_file_name.is_empty(), "Empty expectations log file name" );

    m_log_file.open( log_file_name.begin(), test_or_log ? std::ios::in : std::ios::out );

    BOOST_REQUIRE_MESSAGE( m_log_file.is_open(),
                           "Can't open expectations log file " << log_file_name
                                << " for " << ( m_test_or_log ? "reading" : "writing") );

    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, FILE_SIG );
        ++tit;
        BOOST_CHECK_EQUAL( *tit, ELOG_VER );
    }
    else {
        m_log_file << FILE_SIG << CLMN_SEP << ELOG_VER << LINE_SEP;
    }
}

//____________________________________________________________________________//

bool
expectations_logger::decision_point( const_string, std::size_t )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, DP_SIG ); ++tit;
        return lexical_cast<bool>( *tit );
    }
    else {
        m_log_file << DP_SIG << CLMN_SEP << std::boolalpha << true << LINE_SEP;

        return true;
    }
}

//____________________________________________________________________________//

unsigned
expectations_logger::enter_scope( const_string, std::size_t, const_string scope_name )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, SCOPE_SIG ); ++tit;
        BOOST_CHECK_EQUAL( *tit, scope_name );
    }
    else {
        m_log_file << SCOPE_SIG << CLMN_SEP << scope_name << LINE_SEP;
    }

    return 0;
}

//____________________________________________________________________________//

void
expectations_logger::allocated( const_string, std::size_t, void*, std::size_t s )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, ALLOC_SIG ); ++tit;
        BOOST_CHECK_EQUAL( lexical_cast<std::size_t>( *tit ), s );
    }
    else {
        m_log_file << ALLOC_SIG << CLMN_SEP << s << LINE_SEP;
    }
}

//____________________________________________________________________________//

void
expectations_logger::data_flow( const_string d )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, DATA_SIG ); ++tit;
        BOOST_CHECK_EQUAL( *tit, d );
    }
    else {
        m_log_file << DATA_SIG << CLMN_SEP << d << LINE_SEP;
    }
}

//____________________________________________________________________________//

std::string
expectations_logger::return_value( const_string default_value )
{
    if( m_test_or_log ) {
        std::string line;

        std::getline( m_log_file, line, LINE_SEP );

        const_string cline( line );
        string_token_iterator tit( cline, (dropped_delimeters = CLMN_SEP, kept_delimeters = dt_none));

        BOOST_CHECK_EQUAL( *tit, RETURN_SIG ); ++tit;

        return std::string( tit->begin(), tit->size() );
    }
    else {
        m_log_file << RETURN_SIG << CLMN_SEP << default_value << LINE_SEP;

        return std::string();
    }
}

//____________________________________________________________________________//

// ************************************************************************** //
// **************           logged expectations test           ************** //
// ************************************************************************** //

void BOOST_TEST_DECL
logged_expectations( boost::function<void ()> const& F, const_string log_file_name, bool test_or_log )
{
    expectations_logger el( log_file_name, test_or_log );

    F();
}

//____________________________________________________________________________//

}  // namespace itest
} // namespace boost

#include <boost/test/detail/enable_warnings.hpp>

#endif // not ancient compiler

#endif // BOOST_TEST_LOGGED_EXPECTATIONS_IPP_120905GER
<|endoftext|>


<commit_before>/************************************************************************/
/*                                                                      */
/*        Copyright 2008-2009 by  Ullrich Koethe and Rahul Nair         */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    The VIGRA Website is                                              */
/*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
/*    Please direct questions, bug reports, and contributions to        */
/*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
/*        vigra@informatik.uni-hamburg.de                               */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

#ifndef VIGRA_RANDOM_FOREST_DT_HXX
#define VIGRA_RANDOM_FOREST_DT_HXX

#include <algorithm>
#include <map>
#include <numeric>
#include "vigra/multi_array.hxx"
#include "vigra/mathutil.hxx"
#include "vigra/array_vector.hxx"
#include "vigra/sized_int.hxx"
#include "vigra/matrix.hxx"
#include "vigra/random.hxx"
#include "vigra/functorexpression.hxx"
#include <vector>

#include "rf_common.hxx"
#include "rf_visitors.hxx"
#include "rf_nodeproxy.hxx"
namespace vigra
{

namespace detail
{
 // todo FINALLY DECIDE TO USE CAMEL CASE OR UNDERSCORES !!!!!!
/* decisiontree classifier.
 *
 * This class is actually meant to be used in conjunction with the
 * Random Forest Classifier
 * - My suggestion would be to use the RandomForest classifier with
 *   following parameters instead of directly using this
 *   class (Preprocessing default values etc is handled in there):
 *
 * \code
 *      RandomForest decisionTree(RF_Traits::Options_t()
 *                                  .features_per_node(RF_ALL)
 *                                  .tree_count(1)            );
 * \endcode
 *
 * \todo remove the classCount and featurecount from the topology
 *       array. Pass ext_param_ to the nodes!
 * \todo Use relative addressing of nodes?
 */
class DecisionTree
{
    /* \todo make private?*/
  public:

    /* value type of container array. use whenever referencing it
     */
    typedef Int32 TreeInt;

    ArrayVector<TreeInt>  topology_;
    ArrayVector<double>   parameters_;

    ProblemSpec<> ext_param_;
    unsigned int classCount_;

  public:
    /* \brief Create tree with parameters */
    template<class T>
    DecisionTree(ProblemSpec<T> ext_param)
    :
        ext_param_(ext_param),
        classCount_(ext_param.class_count_)
    {}

    /* clears all memory used.
     */
    void reset(unsigned int classCount = 0)
    {
        if(classCount)
            classCount_ = classCount;
        topology_.clear();
        parameters_.clear();
    }

    /* learn a Tree
     *
     * \tparam  StackEntry_t The Stackentry containing Node/StackEntry_t
     *          Information used during learning. Each Split functor has a
     *          Stack entry associated with it (Split_t::StackEntry_t)
     * \sa RandomForest::learn()
     */
    template <  class U, class C,
                class U2, class C2,
                class StackEntry_t,
                class Stop_t,
                class Split_t,
                class Visitor_t,
                class Random_t >
    void learn(     MultiArrayView<2, U, C> const      & features,
                    MultiArrayView<2, U2, C2> const    & labels,
                    StackEntry_t const &                 stack_entry,
                    Split_t                              split,
                    Stop_t                               stop,
                    Visitor_t &                          visitor,
                    Random_t &                           randint);
    template <  class U, class C,
             class U2, class C2,
             class StackEntry_t,
             class Stop_t,
             class Split_t,
             class Visitor_t,
             class Random_t>
    void continueLearn(   MultiArrayView<2, U, C> const       & features,
                          MultiArrayView<2, U2, C2> const     & labels,
                          StackEntry_t const &                  stack_entry,
                          Split_t                               split,
                          Stop_t                                stop,
                          Visitor_t &                           visitor,
                          Random_t &                            randint,
                          //an index to which the last created exterior node will be moved (because it is not used anymore)
                          int                                   garbaged_child=-1);

    /* is a node a Leaf Node? */
    inline bool isLeafNode(TreeInt in) const
    {
        return (in & LeafNodeTag) == LeafNodeTag;
    }

    /* data driven traversal from root to leaf
     *
     * traverse through tree with data given in features. Use Visitors to
     * collect statistics along the way.
     */
    template<class U, class C, class Visitor_t>
    TreeInt getToLeaf(MultiArrayView<2, U, C> const & features,
                      Visitor_t  & visitor) const
    {
        TreeInt index = 2;
        while(!isLeafNode(topology_[index]))
        {
            visitor.visit_internal_node(*this, index, topology_[index],features);
            switch(topology_[index])
            {
                case i_ThresholdNode:
                {
                    Node<i_ThresholdNode>
                                node(topology_, parameters_, index);
                    index = node.next(features);
                    break;
                }
                case i_HyperplaneNode:
                {
                    Node<i_HyperplaneNode>
                                node(topology_, parameters_, index);
                    index = node.next(features);
                    break;
                }
                case i_HypersphereNode:
                {
                    Node<i_HypersphereNode>
                                node(topology_, parameters_, index);
                    index = node.next(features);
                    break;
                }
#if 0
                // for quick prototyping! has to be implemented.
                case i_VirtualNode:
                {
                    Node<i_VirtualNode>
                                node(topology_, parameters, index);
                    index = node.next(features);
                }
#endif
                default:
                    vigra_fail("DecisionTree::getToLeaf():"
                               "encountered unknown internal Node Type");
            }
        }
        visitor.visit_external_node(*this, index, topology_[index],features);
        return index;
    }
    /* traverse tree to get statistics
     *
     * Tree is traversed in order the Nodes are in memory (i.e. if no
     * relearning//pruning scheme is utilized this will be pre order)
     */
    template<class Visitor_t>
    void traverse_mem_order(Visitor_t visitor) const
    {
        TreeInt index = 2;
        Int32 ii = 0;
        while(index < topology_.size())
        {
            if(isLeafNode(topology_[index]))
            {
                visitor
                    .visit_external_node(*this, index, topology_[index]);
            }
            else
            {
                visitor
                    ._internal_node(*this, index, topology_[index]);
            }
        }
    }

    template<class Visitor_t>
    void traverse_post_order(Visitor_t visitor,  TreeInt start = 2) const
    {
        typedef TinyVector<double, 2> Entry;
        std::vector<Entry > stack;
        std::vector<double> result_stack;
        stack.push_back(Entry(2, 0));
        int addr;
        while(!stack.empty())
        {
            addr = stack.back()[0];
            NodeBase node(topology_, parameters_, stack.back()[0]);
            if(stack.back()[1] == 1)
            {
                stack.pop_back();
                double leftRes = result_stack.back();
                double rightRes = result_stack.back();
                result_stack.pop_back();
                result_stack.pop_back();
                result_stack.push_back(rightRes+ leftRes);
                visitor.visit_internal_node(*this,
                                            addr,
                                            node.typeID(),
                                            rightRes+leftRes);
            }
            else
            {
                if(isLeafNode(node.typeID()))
                {
                    visitor.visit_external_node(*this,
                                                addr,
                                                node.typeID(),
                                                node.weights());
                    stack.pop_back();
                    result_stack.push_back(node.weights());
                }
                else
                {
                    stack.back()[1] = 1;
                    stack.push_back(Entry(node.child(0), 0));
                    stack.push_back(Entry(node.child(1), 0));
                }

            }
        }
    }

    /* same thing as above, without any visitors */
    template<class U, class C>
    TreeInt getToLeaf(MultiArrayView<2, U, C> const & features) const
    {
        ::vigra::rf::visitors::StopVisiting stop;
        return getToLeaf(features, stop);
    }

    template <class U, class C>
    ArrayVector<double>::iterator
    predict(MultiArrayView<2, U, C> const & features) const
    {
        TreeInt nodeindex = getToLeaf(features);
        switch(topology_[nodeindex])
        {
            case e_ConstProbNode:
                return Node<e_ConstProbNode>(topology_,
                                             parameters_,
                                             nodeindex).prob_begin();
                break;
#if 0
            //first make the Logistic regression stuff...
            case e_LogRegProbNode:
                return Node<e_LogRegProbNode>(topology_,
                                              parameters_,
                                              nodeindex).prob_begin();
#endif
            default:
                vigra_fail("DecisionTree::predict() :"
                           " encountered unknown external Node Type");
        }
        return ArrayVector<double>::iterator();
    }

    template <class U, class C>
    Int32 predictLabel(MultiArrayView<2, U, C> const & features) const
    {
        ArrayVector<double>::const_iterator weights = predict(features);
        return argMax(weights, weights+classCount_) - weights;
    }

};

template <  class U, class C,
            class U2, class C2,
            class StackEntry_t,
            class Stop_t,
            class Split_t,
            class Visitor_t,
            class Random_t>
void DecisionTree::learn(   MultiArrayView<2, U, C> const       & features,
                            MultiArrayView<2, U2, C2> const     & labels,
                            StackEntry_t const &                  stack_entry,
                            Split_t                               split,
                            Stop_t                                stop,
                            Visitor_t &                           visitor,
                            Random_t &                            randint)
{
    this->reset();
    topology_.reserve(256);
    parameters_.reserve(256);
    topology_.push_back(features.shape(1));
    topology_.push_back(classCount_);
    continueLearn(features,labels,stack_entry,split,stop,visitor,randint);
}

template <  class U, class C,
            class U2, class C2,
            class StackEntry_t,
            class Stop_t,
            class Split_t,
            class Visitor_t,
            class Random_t>
void DecisionTree::continueLearn(   MultiArrayView<2, U, C> const       & features,
                            MultiArrayView<2, U2, C2> const     & labels,
                            StackEntry_t const &                  stack_entry,
                            Split_t                               split,
                            Stop_t                                stop,
                            Visitor_t &                           visitor,
                            Random_t &                            randint,
                            //an index to which the last created exterior node will be moved (because it is not used anymore)
                            int                                   garbaged_child)
{
    std::vector<StackEntry_t> stack;
    stack.reserve(128);
    ArrayVector<StackEntry_t> child_stack_entry(2, stack_entry);
    stack.push_back(stack_entry);
    size_t last_node_pos = 0;
    StackEntry_t top=stack.back();

    while(!stack.empty())
    {

        // Take an element of the stack. Obvious ain't it?
        top = stack.back();
        stack.pop_back();

        // Make sure no data from the last round has remained in Pipeline;
        child_stack_entry[0].reset();
        child_stack_entry[1].reset();
        split.reset();

        //Either the Stopping criterion decides that the split should
        //produce a Terminal Node or the Split itself decides what
        //kind of node to make
        TreeInt NodeID;

        if(stop(top))
            NodeID = split.makeTerminalNode(features,
                                            labels,
                                            top,
                                            randint);
        else
        {
            //TIC;
            NodeID = split.findBestSplit(features,
                                         labels,
                                         top,
                                         child_stack_entry,
                                         randint);
            //std::cerr << TOC <<" " << NodeID << ";" <<std::endl;
        }

        // do some visiting yawn - just added this comment as eye candy
        // (looks odd otherwise with my syntax highlighting....
        visitor.visit_after_split(*this, split, top,
                                  child_stack_entry[0],
                                  child_stack_entry[1],
                                  features,
                                  labels);

        // Update the Child entries of the parent
        // Using InteriorNodeBase because exact parameter form not needed.
        // look at the Node base before getting scared.
        last_node_pos = topology_.size();
        if(top.leftParent != StackEntry_t::DecisionTreeNoParent)
        {
            NodeBase(topology_,
                     parameters_,
                     top.leftParent).child(0) = last_node_pos;
        }
        else if(top.rightParent != StackEntry_t::DecisionTreeNoParent)
        {
            NodeBase(topology_,
                     parameters_,
                     top.rightParent).child(1) = last_node_pos;
        }

        // Supply the split functor with the Node type it requires.
        // set the address to which the children of this node should point
        // to and push back children onto stack
        if(!isLeafNode(NodeID))
        {
            child_stack_entry[0].leftParent = topology_.size();
            child_stack_entry[1].rightParent = topology_.size();
            child_stack_entry[0].rightParent = -1;
            child_stack_entry[1].leftParent = -1;
            stack.push_back(child_stack_entry[0]);
            stack.push_back(child_stack_entry[1]);
        }

        //copy the newly created node form the split functor to the
        //decision tree.
        NodeBase(split.createNode(), topology_, parameters_ );
    }
    if(garbaged_child!=-1)
    {
        Node<e_ConstProbNode>(topology_,parameters_,garbaged_child).copy(Node<e_ConstProbNode>(topology_,parameters_,last_node_pos));

        int last_parameter_size = Node<e_ConstProbNode>(topology_,parameters_,garbaged_child).parameters_size();
        topology_.resize(last_node_pos);
        parameters_.resize(parameters_.size() - last_parameter_size);

        if(top.leftParent != StackEntry_t::DecisionTreeNoParent)
            NodeBase(topology_,
                     parameters_,
                     top.leftParent).child(0) = garbaged_child;
        else if(top.rightParent != StackEntry_t::DecisionTreeNoParent)
            NodeBase(topology_,
                     parameters_,
                     top.rightParent).child(1) = garbaged_child;
    }
}

} //namespace detail

} //namespace vigra

#endif //VIGRA_RANDOM_FOREST_DT_HXX
<commit_msg>fix clang warning<commit_after>/************************************************************************/
/*                                                                      */
/*        Copyright 2008-2009 by  Ullrich Koethe and Rahul Nair         */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    The VIGRA Website is                                              */
/*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
/*    Please direct questions, bug reports, and contributions to        */
/*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
/*        vigra@informatik.uni-hamburg.de                               */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

#ifndef VIGRA_RANDOM_FOREST_DT_HXX
#define VIGRA_RANDOM_FOREST_DT_HXX

#include <algorithm>
#include <map>
#include <numeric>
#include "vigra/multi_array.hxx"
#include "vigra/mathutil.hxx"
#include "vigra/array_vector.hxx"
#include "vigra/sized_int.hxx"
#include "vigra/matrix.hxx"
#include "vigra/random.hxx"
#include "vigra/functorexpression.hxx"
#include <vector>

#include "rf_common.hxx"
#include "rf_visitors.hxx"
#include "rf_nodeproxy.hxx"
namespace vigra
{

namespace detail
{
 // todo FINALLY DECIDE TO USE CAMEL CASE OR UNDERSCORES !!!!!!
/* decisiontree classifier.
 *
 * This class is actually meant to be used in conjunction with the
 * Random Forest Classifier
 * - My suggestion would be to use the RandomForest classifier with
 *   following parameters instead of directly using this
 *   class (Preprocessing default values etc is handled in there):
 *
 * \code
 *      RandomForest decisionTree(RF_Traits::Options_t()
 *                                  .features_per_node(RF_ALL)
 *                                  .tree_count(1)            );
 * \endcode
 *
 * \todo remove the classCount and featurecount from the topology
 *       array. Pass ext_param_ to the nodes!
 * \todo Use relative addressing of nodes?
 */
class DecisionTree
{
    /* \todo make private?*/
  public:

    /* value type of container array. use whenever referencing it
     */
    typedef Int32 TreeInt;

    ArrayVector<TreeInt>  topology_;
    ArrayVector<double>   parameters_;

    ProblemSpec<> ext_param_;
    unsigned int classCount_;

  public:
    /* \brief Create tree with parameters */
    template<class T>
    DecisionTree(ProblemSpec<T> ext_param)
    :
        ext_param_(ext_param),
        classCount_(ext_param.class_count_)
    {}

    /* clears all memory used.
     */
    void reset(unsigned int classCount = 0)
    {
        if(classCount)
            classCount_ = classCount;
        topology_.clear();
        parameters_.clear();
    }

    /* learn a Tree
     *
     * \tparam  StackEntry_t The Stackentry containing Node/StackEntry_t
     *          Information used during learning. Each Split functor has a
     *          Stack entry associated with it (Split_t::StackEntry_t)
     * \sa RandomForest::learn()
     */
    template <  class U, class C,
                class U2, class C2,
                class StackEntry_t,
                class Stop_t,
                class Split_t,
                class Visitor_t,
                class Random_t >
    void learn(     MultiArrayView<2, U, C> const      & features,
                    MultiArrayView<2, U2, C2> const    & labels,
                    StackEntry_t const &                 stack_entry,
                    Split_t                              split,
                    Stop_t                               stop,
                    Visitor_t &                          visitor,
                    Random_t &                           randint);
    template <  class U, class C,
             class U2, class C2,
             class StackEntry_t,
             class Stop_t,
             class Split_t,
             class Visitor_t,
             class Random_t>
    void continueLearn(   MultiArrayView<2, U, C> const       & features,
                          MultiArrayView<2, U2, C2> const     & labels,
                          StackEntry_t const &                  stack_entry,
                          Split_t                               split,
                          Stop_t                                stop,
                          Visitor_t &                           visitor,
                          Random_t &                            randint,
                          //an index to which the last created exterior node will be moved (because it is not used anymore)
                          int                                   garbaged_child=-1);

    /* is a node a Leaf Node? */
    inline bool isLeafNode(TreeInt in) const
    {
        return (in & LeafNodeTag) == LeafNodeTag;
    }

    /* data driven traversal from root to leaf
     *
     * traverse through tree with data given in features. Use Visitors to
     * collect statistics along the way.
     */
    template<class U, class C, class Visitor_t>
    TreeInt getToLeaf(MultiArrayView<2, U, C> const & features,
                      Visitor_t  & visitor) const
    {
        TreeInt index = 2;
        while(!isLeafNode(topology_[index]))
        {
            visitor.visit_internal_node(*this, index, topology_[index],features);
            switch(topology_[index])
            {
                case i_ThresholdNode:
                {
                    Node<i_ThresholdNode>
                                node(topology_, parameters_, index);
                    index = node.next(features);
                    break;
                }
                case i_HyperplaneNode:
                {
                    Node<i_HyperplaneNode>
                                node(topology_, parameters_, index);
                    index = node.next(features);
                    break;
                }
                case i_HypersphereNode:
                {
                    Node<i_HypersphereNode>
                                node(topology_, parameters_, index);
                    index = node.next(features);
                    break;
                }
#if 0
                // for quick prototyping! has to be implemented.
                case i_VirtualNode:
                {
                    Node<i_VirtualNode>
                                node(topology_, parameters, index);
                    index = node.next(features);
                }
#endif
                default:
                    vigra_fail("DecisionTree::getToLeaf():"
                               "encountered unknown internal Node Type");
            }
        }
        visitor.visit_external_node(*this, index, topology_[index],features);
        return index;
    }
    /* traverse tree to get statistics
     *
     * Tree is traversed in order the Nodes are in memory (i.e. if no
     * relearning//pruning scheme is utilized this will be pre order)
     */
    template<class Visitor_t>
    void traverse_mem_order(Visitor_t visitor) const
    {
        TreeInt index = 2;
        Int32 ii = 0;
        while(index < topology_.size())
        {
            if(isLeafNode(topology_[index]))
            {
                visitor
                    .visit_external_node(*this, index, topology_[index]);
            }
            else
            {
                visitor
                    ._internal_node(*this, index, topology_[index]);
            }
        }
    }

    template<class Visitor_t>
    void traverse_post_order(Visitor_t visitor,  TreeInt start = 2) const
    {
        typedef TinyVector<double, 2> Entry;
        std::vector<Entry > stack;
        std::vector<double> result_stack;
        stack.push_back(Entry(2, 0));
        int addr;
        while(!stack.empty())
        {
            addr = stack.back()[0];
            NodeBase node(topology_, parameters_, stack.back()[0]);
            if(stack.back()[1] == 1)
            {
                stack.pop_back();
                double leftRes = result_stack.back();
                double rightRes = result_stack.back();
                result_stack.pop_back();
                result_stack.pop_back();
                result_stack.push_back(rightRes+ leftRes);
                visitor.visit_internal_node(*this,
                                            addr,
                                            node.typeID(),
                                            rightRes+leftRes);
            }
            else
            {
                if(isLeafNode(node.typeID()))
                {
                    visitor.visit_external_node(*this,
                                                addr,
                                                node.typeID(),
                                                node.weights());
                    stack.pop_back();
                    result_stack.push_back(node.weights());
                }
                else
                {
                    stack.back()[1] = 1;
                    stack.push_back(Entry(node.child(0), 0));
                    stack.push_back(Entry(node.child(1), 0));
                }

            }
        }
    }

    /* same thing as above, without any visitors */
    template<class U, class C>
    TreeInt getToLeaf(MultiArrayView<2, U, C> const & features) const
    {
        ::vigra::rf::visitors::StopVisiting stop;
        return getToLeaf(features, stop);
    }

    template <class U, class C>
    ArrayVector<double>::iterator
    predict(MultiArrayView<2, U, C> const & features) const
    {
        TreeInt nodeindex = getToLeaf(features);
        switch(topology_[nodeindex])
        {
            case e_ConstProbNode:
                return Node<e_ConstProbNode>(topology_,
                                             parameters_,
                                             nodeindex).prob_begin();
                break;
#if 0
            //first make the Logistic regression stuff...
            case e_LogRegProbNode:
                return Node<e_LogRegProbNode>(topology_,
                                              parameters_,
                                              nodeindex).prob_begin();
#endif
            default:
                vigra_fail("DecisionTree::predict() :"
                           " encountered unknown external Node Type");
        }
        return ArrayVector<double>::iterator();
    }

    template <class U, class C>
    Int32 predictLabel(MultiArrayView<2, U, C> const & features) const
    {
        ArrayVector<double>::const_iterator weights = predict(features);
        return argMax(weights, weights+classCount_) - weights;
    }

};

template <  class U, class C,
            class U2, class C2,
            class StackEntry_t,
            class Stop_t,
            class Split_t,
            class Visitor_t,
            class Random_t>
void DecisionTree::learn(   MultiArrayView<2, U, C> const       & features,
                            MultiArrayView<2, U2, C2> const     & labels,
                            StackEntry_t const &                  stack_entry,
                            Split_t                               split,
                            Stop_t                                stop,
                            Visitor_t &                           visitor,
                            Random_t &                            randint)
{
    this->reset();
    topology_.reserve(256);
    parameters_.reserve(256);
    topology_.push_back(features.shape(1));
    topology_.push_back(classCount_);
    continueLearn(features,labels,stack_entry,split,stop,visitor,randint);
}

template <  class U, class C,
            class U2, class C2,
            class StackEntry_t,
            class Stop_t,
            class Split_t,
            class Visitor_t,
            class Random_t>
void DecisionTree::continueLearn(   MultiArrayView<2, U, C> const       & features,
                            MultiArrayView<2, U2, C2> const     & labels,
                            StackEntry_t const &                  stack_entry,
                            Split_t                               split,
                            Stop_t                                stop,
                            Visitor_t &                           visitor,
                            Random_t &                            randint,
                            //an index to which the last created exterior node will be moved (because it is not used anymore)
                            int                                   garbaged_child)
{
    std::vector<StackEntry_t> stack;
    stack.reserve(128);
    ArrayVector<StackEntry_t> child_stack_entry(2, stack_entry);
    stack.push_back(stack_entry);
    size_t last_node_pos = 0;
    StackEntry_t top=stack.back();

    while(!stack.empty())
    {

        // Take an element of the stack. Obvious ain't it?
        top = stack.back();
        stack.pop_back();

        // Make sure no data from the last round has remained in Pipeline;
        child_stack_entry[0].reset();
        child_stack_entry[1].reset();
        split.reset();

        //Either the Stopping criterion decides that the split should
        //produce a Terminal Node or the Split itself decides what
        //kind of node to make
        TreeInt NodeID;

        if(stop(top))
            NodeID = split.makeTerminalNode(features,
                                            labels,
                                            top,
                                            randint);
        else
        {
            //TIC;
            NodeID = split.findBestSplit(features,
                                         labels,
                                         top,
                                         child_stack_entry,
                                         randint);
            //std::cerr << TOC <<" " << NodeID << ";" <<std::endl;
        }

        // do some visiting yawn - just added this comment as eye candy
        // (looks odd otherwise with my syntax highlighting....
        visitor.visit_after_split(*this, split, top,
                                  child_stack_entry[0],
                                  child_stack_entry[1],
                                  features,
                                  labels);

        // Update the Child entries of the parent
        // Using InteriorNodeBase because exact parameter form not needed.
        // look at the Node base before getting scared.
        last_node_pos = topology_.size();
        if(top.leftParent != StackEntry_t::DecisionTreeNoParent)
        {
            NodeBase(topology_,
                     parameters_,
                     top.leftParent).child(0) = last_node_pos;
        }
        else if(top.rightParent != StackEntry_t::DecisionTreeNoParent)
        {
            NodeBase(topology_,
                     parameters_,
                     top.rightParent).child(1) = last_node_pos;
        }

        // Supply the split functor with the Node type it requires.
        // set the address to which the children of this node should point
        // to and push back children onto stack
        if(!isLeafNode(NodeID))
        {
            child_stack_entry[0].leftParent = topology_.size();
            child_stack_entry[1].rightParent = topology_.size();
            child_stack_entry[0].rightParent = -1;
            child_stack_entry[1].leftParent = -1;
            stack.push_back(child_stack_entry[0]);
            stack.push_back(child_stack_entry[1]);
        }

        //copy the newly created node form the split functor to the
        //decision tree.
        NodeBase node(split.createNode(), topology_, parameters_ );
    }
    if(garbaged_child!=-1)
    {
        Node<e_ConstProbNode>(topology_,parameters_,garbaged_child).copy(Node<e_ConstProbNode>(topology_,parameters_,last_node_pos));

        int last_parameter_size = Node<e_ConstProbNode>(topology_,parameters_,garbaged_child).parameters_size();
        topology_.resize(last_node_pos);
        parameters_.resize(parameters_.size() - last_parameter_size);

        if(top.leftParent != StackEntry_t::DecisionTreeNoParent)
            NodeBase(topology_,
                     parameters_,
                     top.leftParent).child(0) = garbaged_child;
        else if(top.rightParent != StackEntry_t::DecisionTreeNoParent)
            NodeBase(topology_,
                     parameters_,
                     top.rightParent).child(1) = garbaged_child;
    }
}

} //namespace detail

} //namespace vigra

#endif //VIGRA_RANDOM_FOREST_DT_HXX
<|endoftext|>


<commit_before>/**
 * @file   llprocesslauncher_test.cpp
 * @author Nat Goodspeed
 * @date   2011-12-19
 * @brief  Test for llprocesslauncher.
 *
 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
 * Copyright (c) 2011, Linden Research, Inc.
 * $/LicenseInfo$
 */

// Precompiled header
#include "linden_common.h"
// associated header
#include "llprocesslauncher.h"
// STL headers
#include <vector>
// std headers
// external library headers
#include "llapr.h"
#include "apr_thread_proc.h"
#include "apr_file_io.h"
// other Linden headers
#include "../test/lltut.h"

class APR
{
public:
    APR():
        pool(NULL)
    {
        apr_initialize();
        apr_pool_create(&pool, NULL);
    }

    ~APR()
    {
        apr_terminate();
    }

    std::string strerror(apr_status_t rv)
    {
        char errbuf[256];
        apr_strerror(rv, errbuf, sizeof(errbuf));
        return errbuf;
    }

    apr_pool_t *pool;
};

/*****************************************************************************
*   TUT
*****************************************************************************/
namespace tut
{
    struct llprocesslauncher_data
    {
        void aprchk(apr_status_t rv)
        {
            ensure_equals(apr.strerror(rv), rv, APR_SUCCESS);
        }

        APR apr;
    };
    typedef test_group<llprocesslauncher_data> llprocesslauncher_group;
    typedef llprocesslauncher_group::object object;
    llprocesslauncher_group llprocesslaunchergrp("llprocesslauncher");

    template<> template<>
    void object::test<1>()
    {
        set_test_name("raw APR nonblocking I/O");

        apr_procattr_t *procattr = NULL;
        aprchk(apr_procattr_create(&procattr, apr.pool));
        aprchk(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK));
        aprchk(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));

        std::vector<const char*> argv;
        apr_proc_t child;
        argv.push_back("python");
        argv.push_back("-c");
        argv.push_back("raise RuntimeError('Hello from Python!')");
        argv.push_back(NULL);

        aprchk(apr_proc_create(&child, argv[0],
                               static_cast<const char* const*>(&argv[0]),
                               NULL, // if we wanted to pass explicit environment
                               procattr,
                               apr.pool));

        typedef std::pair<std::string, apr_file_t*> DescFile;
        typedef std::vector<DescFile> DescFileVec;
        DescFileVec outfiles;
        outfiles.push_back(DescFile("out", child.out));
        outfiles.push_back(DescFile("err", child.err));

        while (! outfiles.empty())
        {
            DescFileVec iterfiles(outfiles);
            for (size_t i = 0; i < iterfiles.size(); ++i)
            {
                char buf[4096];

                apr_status_t rv = apr_file_gets(buf, sizeof(buf), iterfiles[i].second);
                if (APR_STATUS_IS_EOF(rv))
                {
                    std::cout << "(EOF on " << iterfiles[i].first << ")\n";
                    outfiles.erase(outfiles.begin() + i);
                    continue;
                }
                if (rv != APR_SUCCESS)
                {
                    std::cout << "(waiting; apr_file_gets(" << iterfiles[i].first << ") => " << rv << ": " << apr.strerror(rv) << ")\n";
                    continue;
                }
                // Is it even possible to get APR_SUCCESS but read 0 bytes?
                // Hope not, but defend against that anyway.
                if (buf[0])
                {
                    std::cout << iterfiles[i].first << ": " << buf;
                    // Just for pretty output... if we only read a partial
                    // line, terminate it.
                    if (buf[strlen(buf) - 1] != '\n')
                        std::cout << "...\n";
                }
            }
            sleep(1);
        }
        apr_file_close(child.in);
        apr_file_close(child.out);
        apr_file_close(child.err);

        int rc = 0;
        apr_exit_why_e why;
        apr_status_t rv;
        while (! APR_STATUS_IS_CHILD_DONE(rv = apr_proc_wait(&child, &rc, &why, APR_NOWAIT)))
        {
            std::cout << "child not done (" << rv << "): " << apr.strerror(rv) << '\n';
            sleep(0.5);
        }
        std::cout << "child done: rv = " << rv << " (" << apr.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
    }
} // namespace tut
<commit_msg>Fix llprocesslauncher_test.cpp to work on Windows.<commit_after>/**
 * @file   llprocesslauncher_test.cpp
 * @author Nat Goodspeed
 * @date   2011-12-19
 * @brief  Test for llprocesslauncher.
 *
 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
 * Copyright (c) 2011, Linden Research, Inc.
 * $/LicenseInfo$
 */

// Precompiled header
#include "linden_common.h"
// associated header
#define WIN32_LEAN_AND_MEAN
#include "llprocesslauncher.h"
// STL headers
#include <vector>
// std headers
// external library headers
#include "llapr.h"
#include "apr_thread_proc.h"
#include "apr_file_io.h"
// other Linden headers
#include "../test/lltut.h"

#if defined(LL_WINDOWS)
#define sleep _sleep
#endif

class APR
{
public:
    APR():
        pool(NULL)
    {
        apr_initialize();
        apr_pool_create(&pool, NULL);
    }

    ~APR()
    {
        apr_terminate();
    }

    std::string strerror(apr_status_t rv)
    {
        char errbuf[256];
        apr_strerror(rv, errbuf, sizeof(errbuf));
        return errbuf;
    }

    apr_pool_t *pool;
};

/*****************************************************************************
*   TUT
*****************************************************************************/
namespace tut
{
    struct llprocesslauncher_data
    {
        void aprchk(apr_status_t rv)
        {
            ensure_equals(apr.strerror(rv), rv, APR_SUCCESS);
        }

        APR apr;
    };
    typedef test_group<llprocesslauncher_data> llprocesslauncher_group;
    typedef llprocesslauncher_group::object object;
    llprocesslauncher_group llprocesslaunchergrp("llprocesslauncher");

    template<> template<>
    void object::test<1>()
    {
        set_test_name("raw APR nonblocking I/O");

        apr_procattr_t *procattr = NULL;
        aprchk(apr_procattr_create(&procattr, apr.pool));
        aprchk(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK));
        aprchk(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));

        std::vector<const char*> argv;
        apr_proc_t child;
        argv.push_back("python");
        argv.push_back("-c");
        argv.push_back("raise RuntimeError('Hello from Python!')");
        argv.push_back(NULL);

        aprchk(apr_proc_create(&child, argv[0],
                               static_cast<const char* const*>(&argv[0]),
                               NULL, // if we wanted to pass explicit environment
                               procattr,
                               apr.pool));

        typedef std::pair<std::string, apr_file_t*> DescFile;
        typedef std::vector<DescFile> DescFileVec;
        DescFileVec outfiles;
        outfiles.push_back(DescFile("out", child.out));
        outfiles.push_back(DescFile("err", child.err));

        while (! outfiles.empty())
        {
            DescFileVec iterfiles(outfiles);
            for (size_t i = 0; i < iterfiles.size(); ++i)
            {
                char buf[4096];

                apr_status_t rv = apr_file_gets(buf, sizeof(buf), iterfiles[i].second);
                if (APR_STATUS_IS_EOF(rv))
                {
                    std::cout << "(EOF on " << iterfiles[i].first << ")\n";
                    outfiles.erase(outfiles.begin() + i);
                    continue;
                }
                if (rv != APR_SUCCESS)
                {
                    std::cout << "(waiting; apr_file_gets(" << iterfiles[i].first << ") => " << rv << ": " << apr.strerror(rv) << ")\n";
                    continue;
                }
                // Is it even possible to get APR_SUCCESS but read 0 bytes?
                // Hope not, but defend against that anyway.
                if (buf[0])
                {
                    std::cout << iterfiles[i].first << ": " << buf;
                    // Just for pretty output... if we only read a partial
                    // line, terminate it.
                    if (buf[strlen(buf) - 1] != '\n')
                        std::cout << "...\n";
                }
            }
            sleep(1);
        }
        apr_file_close(child.in);
        apr_file_close(child.out);
        apr_file_close(child.err);

        int rc = 0;
        apr_exit_why_e why;
        apr_status_t rv;
        while (! APR_STATUS_IS_CHILD_DONE(rv = apr_proc_wait(&child, &rc, &why, APR_NOWAIT)))
        {
            std::cout << "child not done (" << rv << "): " << apr.strerror(rv) << '\n';
            sleep(0.5);
        }
        std::cout << "child done: rv = " << rv << " (" << apr.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
    }
} // namespace tut
<|endoftext|>