<commit_before>/**
 * @file max_ip_impl.hpp
 * @author Ryan Curtin
 *
 * Implementation of the MaxIP class (maximum inner product search).
 */
#ifndef __MLPACK_METHODS_MAXIP_MAX_IP_IMPL_HPP
#define __MLPACK_METHODS_MAXIP_MAX_IP_IMPL_HPP

// In case it hasn't yet been included.
#include "max_ip.hpp"

#include <mlpack/core/tree/traversers/single_cover_tree_traverser.hpp>
#include "max_ip_rules.hpp"

namespace mlpack {
namespace maxip {

template<typename KernelType>
MaxIP<KernelType>::MaxIP(const arma::mat& referenceSet,
                         bool single,
                         bool naive) :
    referenceSet(referenceSet),
    querySet(referenceSet), // Gotta point it somewhere...
    queryTree(NULL),
    single(single),
    naive(naive)
{
  Timer::Start("tree_building");

  // Build the tree.  Could we do this in the initialization list?
  if (naive)
    referenceTree = NULL;
  else
    referenceTree = new tree::CoverTree<IPMetric<KernelType> >(referenceSet);

  Timer::Stop("tree_building");
}

template<typename KernelType>
MaxIP<KernelType>::MaxIP(const arma::mat& referenceSet,
                         const arma::mat& querySet,
                         bool single,
                         bool naive) :
    referenceSet(referenceSet),
    querySet(querySet),
    single(single),
    naive(naive)
{
  Timer::Start("tree_building");

  // Build the trees.  Could we do this in the initialization lists?
  if (naive)
    referenceTree = NULL;
  else
    referenceTree = new tree::CoverTree<IPMetric<KernelType> >(referenceSet);

  if (single || naive)
    queryTree = NULL;
  else
    queryTree = new tree::CoverTree<IPMetric<KernelType> >(querySet);

  Timer::Stop("tree_building");
}

template<typename KernelType>
MaxIP<KernelType>::~MaxIP()
{
  if (queryTree)
    delete queryTree;
  if (referenceTree)
    delete referenceTree;
}

template<typename KernelType>
void MaxIP<KernelType>::Search(const size_t k,
                               arma::Mat<size_t>& indices,
                               arma::mat& products)
{
  // No remapping will be necessary.
  indices.set_size(k, querySet.n_cols);
  products.set_size(k, querySet.n_cols);

  Timer::Start("computing_products");

  // Naive implementation.
  if (naive)
  {
    // Simple double loop.  Stupid, slow, but a good benchmark.
    for (size_t q = 0; q < querySet.n_cols; ++q)
    {
      for (size_t r = 0; r < referenceSet.n_cols; ++r)
      {
        const double eval = KernelType::Evaluate(querySet.unsafe_col(q),
                                                 referenceSet.unsafe_col(r));

        size_t insertPosition;
        for (insertPosition = 0; insertPosition < indices.n_rows;
            ++insertPosition)
          if (eval > products(insertPosition, q))
            break;

        if (insertPosition < indices.n_rows)
          InsertNeighbor(indices, products, q, insertPosition, r, eval);
      }
    }

    Timer::Stop("computing_products");
    return;
  }

  // Single-tree implementation.
  if (single)
  {
    // Calculate number of pruned nodes.
    size_t numPrunes = 0;

    // Precalculate query products ( || q || for all q).
    arma::vec queryProducts(querySet.n_cols);
    for (size_t queryIndex = 0; queryIndex < querySet.n_cols; ++queryIndex)
      queryProducts[queryIndex] = KernelType::Evaluate(
          querySet.unsafe_col(queryIndex), querySet.unsafe_col(queryIndex));

    // Screw the CoverTreeTraverser, we'll implement it by hand.
    for (size_t queryIndex = 0; queryIndex < querySet.n_cols; ++queryIndex)
    {
      std::queue<tree::CoverTree<IPMetric<KernelType> >*> pointQueue;
      std::queue<size_t> parentQueue;
      std::queue<double> parentEvalQueue;
      pointQueue.push(referenceTree);
      parentQueue.push(size_t() - 1); // Has no parent.
      parentEvalQueue.push(0); // No possible parent evaluation.

      tree::CoverTree<IPMetric<KernelType> >* referenceNode;
      size_t currentParent;
      double currentParentEval;
      double eval; // Kernel evaluation.

      while (!pointQueue.empty())
      {
        // Get the information for this node.
        referenceNode = pointQueue.front();
        currentParent = parentQueue.front();
        currentParentEval = parentEvalQueue.front();

        pointQueue.pop();
        parentQueue.pop();
        parentEvalQueue.pop();

        // See if this has the same parent.
        if (referenceNode->Point() == currentParent)
        {
          // We don't have to evaluate the kernel again.
          eval = currentParentEval;
        }
        else
        {
          // Evaluate the kernel.  Then see if it is a result to keep.
          eval = KernelType::Evaluate(querySet.unsafe_col(queryIndex),
              referenceSet.unsafe_col(referenceNode->Point()));

          // Is the result good enough to be saved?
          if (eval > products(products.n_rows - 1, queryIndex))
          {
            // Figure out where to insert.
            size_t insertPosition = 0;
            for ( ; insertPosition < products.n_rows - 1; ++insertPosition)
              if (eval > products(insertPosition, queryIndex))
                break;

            // We are guaranteed that insertPosition is valid.
            InsertNeighbor(indices, products, queryIndex, insertPosition,
                referenceNode->Point(), eval);
          }
        }

        // Now discover if we can prune this node or not.
        double maxProduct = eval + std::pow(referenceNode->ExpansionConstant(),
            referenceNode->Scale() + 1) * queryProducts[queryIndex];

        if (maxProduct > products(products.n_rows - 1, queryIndex))
        {
          // We can't prune.  So add our children.
          for (size_t i = 0; i < referenceNode->NumChildren(); ++i)
          {
            pointQueue.push(&(referenceNode->Child(i)));
            parentQueue.push(referenceNode->Point());
            parentEvalQueue.push(eval);
          }
        }
        else
        {
          numPrunes++;
        }
      }
    }

    Log::Info << "Pruned " << numPrunes << " nodes." << std::endl;

    Timer::Stop("computing_products");
    return;
  }

  // Double-tree implementation.
  Log::Fatal << "Dual-tree search not implemented yet... oops..." << std::endl;
}

/**
 * Helper function to insert a point into the neighbors and distances matrices.
 *
 * @param queryIndex Index of point whose neighbors we are inserting into.
 * @param pos Position in list to insert into.
 * @param neighbor Index of reference point which is being inserted.
 * @param distance Distance from query point to reference point.
 */
template<typename KernelType>
void MaxIP<KernelType>::InsertNeighbor(arma::Mat<size_t>& indices,
                                       arma::mat& products,
                                       const size_t queryIndex,
                                       const size_t pos,
                                       const size_t neighbor,
                                       const double distance)
{
  // We only memmove() if there is actually a need to shift something.
  if (pos < (products.n_rows - 1))
  {
    int len = (products.n_rows - 1) - pos;
    memmove(products.colptr(queryIndex) + (pos + 1),
        products.colptr(queryIndex) + pos,
        sizeof(double) * len);
    memmove(indices.colptr(queryIndex) + (pos + 1),
        indices.colptr(queryIndex) + pos,
        sizeof(size_t) * len);
  }

  // Now put the new information in the right index.
  products(pos, queryIndex) = distance;
  indices(pos, queryIndex) = neighbor;
}

}; // namespace maxip
}; // namespace mlpack

#endif
<commit_msg>This file doesn't exist.  I didn't need it.<commit_after>/**
 * @file max_ip_impl.hpp
 * @author Ryan Curtin
 *
 * Implementation of the MaxIP class (maximum inner product search).
 */
#ifndef __MLPACK_METHODS_MAXIP_MAX_IP_IMPL_HPP
#define __MLPACK_METHODS_MAXIP_MAX_IP_IMPL_HPP

// In case it hasn't yet been included.
#include "max_ip.hpp"

#include "max_ip_rules.hpp"

namespace mlpack {
namespace maxip {

template<typename KernelType>
MaxIP<KernelType>::MaxIP(const arma::mat& referenceSet,
                         bool single,
                         bool naive) :
    referenceSet(referenceSet),
    querySet(referenceSet), // Gotta point it somewhere...
    queryTree(NULL),
    single(single),
    naive(naive)
{
  Timer::Start("tree_building");

  // Build the tree.  Could we do this in the initialization list?
  if (naive)
    referenceTree = NULL;
  else
    referenceTree = new tree::CoverTree<IPMetric<KernelType> >(referenceSet);

  Timer::Stop("tree_building");
}

template<typename KernelType>
MaxIP<KernelType>::MaxIP(const arma::mat& referenceSet,
                         const arma::mat& querySet,
                         bool single,
                         bool naive) :
    referenceSet(referenceSet),
    querySet(querySet),
    single(single),
    naive(naive)
{
  Timer::Start("tree_building");

  // Build the trees.  Could we do this in the initialization lists?
  if (naive)
    referenceTree = NULL;
  else
    referenceTree = new tree::CoverTree<IPMetric<KernelType> >(referenceSet);

  if (single || naive)
    queryTree = NULL;
  else
    queryTree = new tree::CoverTree<IPMetric<KernelType> >(querySet);

  Timer::Stop("tree_building");
}

template<typename KernelType>
MaxIP<KernelType>::~MaxIP()
{
  if (queryTree)
    delete queryTree;
  if (referenceTree)
    delete referenceTree;
}

template<typename KernelType>
void MaxIP<KernelType>::Search(const size_t k,
                               arma::Mat<size_t>& indices,
                               arma::mat& products)
{
  // No remapping will be necessary.
  indices.set_size(k, querySet.n_cols);
  products.set_size(k, querySet.n_cols);

  Timer::Start("computing_products");

  // Naive implementation.
  if (naive)
  {
    // Simple double loop.  Stupid, slow, but a good benchmark.
    for (size_t q = 0; q < querySet.n_cols; ++q)
    {
      for (size_t r = 0; r < referenceSet.n_cols; ++r)
      {
        const double eval = KernelType::Evaluate(querySet.unsafe_col(q),
                                                 referenceSet.unsafe_col(r));

        size_t insertPosition;
        for (insertPosition = 0; insertPosition < indices.n_rows;
            ++insertPosition)
          if (eval > products(insertPosition, q))
            break;

        if (insertPosition < indices.n_rows)
          InsertNeighbor(indices, products, q, insertPosition, r, eval);
      }
    }

    Timer::Stop("computing_products");
    return;
  }

  // Single-tree implementation.
  if (single)
  {
    // Calculate number of pruned nodes.
    size_t numPrunes = 0;

    // Precalculate query products ( || q || for all q).
    arma::vec queryProducts(querySet.n_cols);
    for (size_t queryIndex = 0; queryIndex < querySet.n_cols; ++queryIndex)
      queryProducts[queryIndex] = KernelType::Evaluate(
          querySet.unsafe_col(queryIndex), querySet.unsafe_col(queryIndex));

    // Screw the CoverTreeTraverser, we'll implement it by hand.
    for (size_t queryIndex = 0; queryIndex < querySet.n_cols; ++queryIndex)
    {
      std::queue<tree::CoverTree<IPMetric<KernelType> >*> pointQueue;
      std::queue<size_t> parentQueue;
      std::queue<double> parentEvalQueue;
      pointQueue.push(referenceTree);
      parentQueue.push(size_t() - 1); // Has no parent.
      parentEvalQueue.push(0); // No possible parent evaluation.

      tree::CoverTree<IPMetric<KernelType> >* referenceNode;
      size_t currentParent;
      double currentParentEval;
      double eval; // Kernel evaluation.

      while (!pointQueue.empty())
      {
        // Get the information for this node.
        referenceNode = pointQueue.front();
        currentParent = parentQueue.front();
        currentParentEval = parentEvalQueue.front();

        pointQueue.pop();
        parentQueue.pop();
        parentEvalQueue.pop();

        // See if this has the same parent.
        if (referenceNode->Point() == currentParent)
        {
          // We don't have to evaluate the kernel again.
          eval = currentParentEval;
        }
        else
        {
          // Evaluate the kernel.  Then see if it is a result to keep.
          eval = KernelType::Evaluate(querySet.unsafe_col(queryIndex),
              referenceSet.unsafe_col(referenceNode->Point()));

          // Is the result good enough to be saved?
          if (eval > products(products.n_rows - 1, queryIndex))
          {
            // Figure out where to insert.
            size_t insertPosition = 0;
            for ( ; insertPosition < products.n_rows - 1; ++insertPosition)
              if (eval > products(insertPosition, queryIndex))
                break;

            // We are guaranteed that insertPosition is valid.
            InsertNeighbor(indices, products, queryIndex, insertPosition,
                referenceNode->Point(), eval);
          }
        }

        // Now discover if we can prune this node or not.
        double maxProduct = eval + std::pow(referenceNode->ExpansionConstant(),
            referenceNode->Scale() + 1) * queryProducts[queryIndex];

        if (maxProduct > products(products.n_rows - 1, queryIndex))
        {
          // We can't prune.  So add our children.
          for (size_t i = 0; i < referenceNode->NumChildren(); ++i)
          {
            pointQueue.push(&(referenceNode->Child(i)));
            parentQueue.push(referenceNode->Point());
            parentEvalQueue.push(eval);
          }
        }
        else
        {
          numPrunes++;
        }
      }
    }

    Log::Info << "Pruned " << numPrunes << " nodes." << std::endl;

    Timer::Stop("computing_products");
    return;
  }

  // Double-tree implementation.
  Log::Fatal << "Dual-tree search not implemented yet... oops..." << std::endl;
}

/**
 * Helper function to insert a point into the neighbors and distances matrices.
 *
 * @param queryIndex Index of point whose neighbors we are inserting into.
 * @param pos Position in list to insert into.
 * @param neighbor Index of reference point which is being inserted.
 * @param distance Distance from query point to reference point.
 */
template<typename KernelType>
void MaxIP<KernelType>::InsertNeighbor(arma::Mat<size_t>& indices,
                                       arma::mat& products,
                                       const size_t queryIndex,
                                       const size_t pos,
                                       const size_t neighbor,
                                       const double distance)
{
  // We only memmove() if there is actually a need to shift something.
  if (pos < (products.n_rows - 1))
  {
    int len = (products.n_rows - 1) - pos;
    memmove(products.colptr(queryIndex) + (pos + 1),
        products.colptr(queryIndex) + pos,
        sizeof(double) * len);
    memmove(indices.colptr(queryIndex) + (pos + 1),
        indices.colptr(queryIndex) + pos,
        sizeof(size_t) * len);
  }

  // Now put the new information in the right index.
  products(pos, queryIndex) = distance;
  indices(pos, queryIndex) = neighbor;
}

}; // namespace maxip
}; // namespace mlpack

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2012 Plenluno All rights reserved.

#include <assert.h>
#include <vector>

#include "libj/js_regexp.h"
#include "libj/undefined.h"
#include "./glue/regexp.h"

namespace libj {

static glue::RegExp::U16String toU16String(String::CPtr str) {
    if (Type<char16_t>::id() == Type<uint16_t>::id()) {
        return str->toStdU16String();
    } else {
        glue::RegExp::U16String s;
        std::u16string ss = str->toStdU16String();
        for (size_t i = 0; i < ss.length(); i++) {
            s.push_back(ss[i]);
        }
        return s;
    }
}

class JsRegExpImpl : public JsRegExp {
 public:
    static Ptr create(String::CPtr pattern, UInt flags) {
        JsRegExpImpl* impl = new JsRegExpImpl(pattern, flags);
        if (impl->re_) {
            Ptr p(impl);
            return p;
        } else {
            return null();
        }
    }

    Boolean global() const {
        return re_->global();
    }

    Boolean ignoreCase() const {
        return re_->ignoreCase();
    }

    Boolean multiline() const {
        return re_->multiline();
    }

    String::CPtr source() const {
        return pattern_;
    }

    JsArray::Ptr exec(String::CPtr str) const {
        static const String::CPtr index = String::create("index");
        static const String::CPtr input = String::create("input");

        if (!str) {
            return JsArray::null();
        }

        std::vector<int> captures;
        if (!re_->execute(toU16String(str), 0, captures)) {
            return JsArray::null();
        }

        JsArray::Ptr res = JsArray::create();
        Size len = captures.size();
        assert(len > 0);
        for (Size i = 0; i < len; i += 2) {
            if (captures[i] >= 0 &&
                captures[i+1] >= 0 &&
                captures[i] < captures[i+1] &&
                captures[i+1] <= static_cast<int>(str->length())) {
                res->add(str->substring(captures[i], captures[i+1]));
            } else {
                res->add(Undefined::instance());
            }
        }
        res->setProperty(input, str);
        res->setProperty(index, captures[0]);
        return res;
    }

    Boolean test(String::CPtr str) const {
        return !!exec(str);
    }

 private:
    JsObject::Ptr obj_;
    String::CPtr pattern_;
    glue::RegExp* re_;

    JsRegExpImpl(String::CPtr pattern, UInt flags)
        : obj_(JsObject::create())
        , pattern_(pattern)
        , re_(glue::RegExp::create(toU16String(pattern), flags)) {}

 public:
    ~JsRegExpImpl() {
        delete re_;
    }

    LIBJ_JS_OBJECT_IMPL(obj_);
};

JsRegExp::Ptr JsRegExp::create(String::CPtr pattern, UInt flags) {
    return JsRegExpImpl::create(pattern, flags);
}

}  // namespace libj
<commit_msg>use LIBJ_USE_CXX11<commit_after>// Copyright (c) 2012 Plenluno All rights reserved.

#include <assert.h>
#include <vector>

#include "libj/js_regexp.h"
#include "libj/undefined.h"
#include "./glue/regexp.h"

namespace libj {

static glue::RegExp::U16String toU16String(String::CPtr str) {
#ifdef LIBJ_USE_CXX11
    glue::RegExp::U16String s;
    std::u16string ss = str->toStdU16String();
    for (size_t i = 0; i < ss.length(); i++) {
        s.push_back(ss[i]);
    }
    return s;
#else
    return str->toStdU16String();
#endif
}

class JsRegExpImpl : public JsRegExp {
 public:
    static Ptr create(String::CPtr pattern, UInt flags) {
        JsRegExpImpl* impl = new JsRegExpImpl(pattern, flags);
        if (impl->re_) {
            Ptr p(impl);
            return p;
        } else {
            return null();
        }
    }

    Boolean global() const {
        return re_->global();
    }

    Boolean ignoreCase() const {
        return re_->ignoreCase();
    }

    Boolean multiline() const {
        return re_->multiline();
    }

    String::CPtr source() const {
        return pattern_;
    }

    JsArray::Ptr exec(String::CPtr str) const {
        static const String::CPtr index = String::create("index");
        static const String::CPtr input = String::create("input");

        if (!str) {
            return JsArray::null();
        }

        std::vector<int> captures;
        if (!re_->execute(toU16String(str), 0, captures)) {
            return JsArray::null();
        }

        JsArray::Ptr res = JsArray::create();
        Size len = captures.size();
        assert(len > 0);
        for (Size i = 0; i < len; i += 2) {
            if (captures[i] >= 0 &&
                captures[i+1] >= 0 &&
                captures[i] < captures[i+1] &&
                captures[i+1] <= static_cast<int>(str->length())) {
                res->add(str->substring(captures[i], captures[i+1]));
            } else {
                res->add(Undefined::instance());
            }
        }
        res->setProperty(input, str);
        res->setProperty(index, captures[0]);
        return res;
    }

    Boolean test(String::CPtr str) const {
        return !!exec(str);
    }

 private:
    JsObject::Ptr obj_;
    String::CPtr pattern_;
    glue::RegExp* re_;

    JsRegExpImpl(String::CPtr pattern, UInt flags)
        : obj_(JsObject::create())
        , pattern_(pattern)
        , re_(glue::RegExp::create(toU16String(pattern), flags)) {}

 public:
    ~JsRegExpImpl() {
        delete re_;
    }

    LIBJ_JS_OBJECT_IMPL(obj_);
};

JsRegExp::Ptr JsRegExp::create(String::CPtr pattern, UInt flags) {
    return JsRegExpImpl::create(pattern, flags);
}

}  // namespace libj
<|endoftext|>


<commit_before>/*
 * opencog/util/Logger.cc
 *
 * Copyright (C) 2002-2007 Novamente LLC
 * Copyright (C) 2008 by Singularity Institute for Artificial Intelligence
 * All Rights Reserved
 *
 * Written by Andre Senna <senna@vettalabs.com>
 *            Gustavo Gama <gama@vettalabs.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "Logger.h"

#include <execinfo.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>

#ifdef WIN32
#include <winsock2.h>
#undef ERROR
#undef DEBUG
#else
#include <sys/time.h>
#endif

#include <opencog/util/platform.h>

using namespace opencog;

// messages greater than this will be truncated
#define MAX_PRINTF_STYLE_MESSAGE_SIZE (1<<15)
const char* levelStrings[] = {"ERROR", "WARN", "INFO", "DEBUG", "FINE"};

Logger::~Logger()
{
    if (f != NULL) fclose(f);
}

Logger::Logger(const std::string &fileName, Logger::Level level, bool timestampEnabled)
{
    this->fileName.assign(fileName);
    this->currentLevel = level;
    this->timestampEnabled = timestampEnabled;
    this->printToStdout = false;

    this->logEnabled = true;
    this->f = NULL;

    pthread_mutex_init(&lock, NULL);
}

// ***********************************************/
// API

void Logger::setLevel(Logger::Level newLevel)
{
    currentLevel = newLevel;
}

Logger::Level Logger::getLevel() const
{
    return currentLevel;
}

void Logger::setFilename(const std::string& s)
{
    fileName.assign(s);
    if (f != NULL) fclose(f);
    f = NULL;
    enable();
}

const std::string& Logger::getFilename()
{
    return fileName;
}

void Logger::setTimestampFlag(bool flag)
{
    timestampEnabled = flag;
}

void Logger::setPrintToStdoutFlag(bool flag)
{
    printToStdout = flag;
}

void Logger::enable()
{
    logEnabled = true;
}

void Logger::disable()
{
    logEnabled = false;
}

static void prt_backtrace(FILE *fh)
{
#define BT_BUFSZ 50
	void *bt_buf[BT_BUFSZ];

	int stack_depth = backtrace(bt_buf, BT_BUFSZ);
	char **syms = backtrace_symbols(bt_buf, stack_depth);

	// Start printing at a bit into the stack, so as to avoid recording
	// the logger functions in the stack trace.
	for (int i=2; i< stack_depth; i++)
	{
		fprintf(fh, "%s\n", syms[i]);
	}
	free(syms);
}

void Logger::log(Logger::Level level, const std::string &txt)
{
    if (!logEnabled) return;
    // delay opening the file until the first logging statement is issued;
    // this allows us to set the main logger's filename without creating
    // a useless log file with the default filename
    if (f == NULL) {
        if ((f = fopen(fileName.c_str(), "a")) == NULL) {
            fprintf(stderr, "[ERROR] Unable to open log file \"%s\"\n", fileName.c_str());
            disable();
            return;
        } else enable();
    }

    if (level <= currentLevel) {
        pthread_mutex_lock(&lock);
        if (timestampEnabled) {
            char timestamp[64];
            struct timeval stv;
            struct tm stm;

            ::gettimeofday(&stv, NULL);
            time_t t = stv.tv_sec;
            gmtime_r(&t, &stm);
            strftime(timestamp, sizeof(timestamp), "%F %T", &stm);
            fprintf(f, "[%s:%03ld] ", timestamp, stv.tv_usec / 1000);
            if (printToStdout) fprintf(stdout, "[%s:%03ld] ", timestamp, stv.tv_usec / 1000);
        }

        fprintf(f, "[%s] %s\n", getLevelString(level), txt.c_str());
        if (printToStdout) fprintf(stdout, "[%s] %s\n", getLevelString(level), txt.c_str());
        fflush(f);

        // Print a backtrace for warnings and errors.
        if (level <= WARN)
        {
            prt_backtrace(f);
        }
        fflush(f);
        pthread_mutex_unlock(&lock);
    }
}
void Logger::error(const std::string &txt)
{
    log(ERROR, txt);
}
void Logger::warn (const std::string &txt)
{
    log(WARN,  txt);
}
void Logger::info (const std::string &txt)
{
    log(INFO,  txt);
}
void Logger::debug(const std::string &txt)
{
    log(DEBUG, txt);
}
void Logger::fine (const std::string &txt)
{
    log(FINE,  txt);
}

void Logger::logva(Logger::Level level, const char *fmt, va_list args)
{
    if (level <= currentLevel) {
        char buffer[MAX_PRINTF_STYLE_MESSAGE_SIZE];
        vsnprintf(buffer, sizeof(buffer), fmt, args);
        std::string msg = buffer;
        log(level, msg);
    }
}

void Logger::log(Logger::Level level, const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(level, fmt, args); va_end(args);
}
void Logger::error(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(ERROR, fmt, args); va_end(args);
}
void Logger::warn (const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(WARN,  fmt, args); va_end(args);
}
void Logger::info (const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(INFO,  fmt, args); va_end(args);
}
void Logger::debug(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(DEBUG, fmt, args); va_end(args);
}
void Logger::fine (const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(FINE,  fmt, args); va_end(args);
}

const char* Logger::getLevelString(const Logger::Level level)
{
    return levelStrings[level];
}

const Logger::Level Logger::getLevelFromString(const std::string& levelStr)
{
    unsigned int nLevels = sizeof(levelStrings) / sizeof(levelStrings[0]);
    for (unsigned int i = 0; i < nLevels; ++i) {
        if (strcasecmp(levelStrings[i], levelStr.c_str()) == 0)
            return ((Logger::Level) i);
    }
    return ((Logger::Level) - 1);
}

// create and return the single instance
Logger& opencog::logger()
{
    static Logger instance;
    return instance;
}
<commit_msg>beutify the stack trace printing message just a little bit.<commit_after>/*
 * opencog/util/Logger.cc
 *
 * Copyright (C) 2002-2007 Novamente LLC
 * Copyright (C) 2008 by Singularity Institute for Artificial Intelligence
 * All Rights Reserved
 *
 * Written by Andre Senna <senna@vettalabs.com>
 *            Gustavo Gama <gama@vettalabs.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "Logger.h"

#include <execinfo.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>

#ifdef WIN32
#include <winsock2.h>
#undef ERROR
#undef DEBUG
#else
#include <sys/time.h>
#endif

#include <opencog/util/platform.h>

using namespace opencog;

// messages greater than this will be truncated
#define MAX_PRINTF_STYLE_MESSAGE_SIZE (1<<15)
const char* levelStrings[] = {"ERROR", "WARN", "INFO", "DEBUG", "FINE"};

Logger::~Logger()
{
    if (f != NULL) fclose(f);
}

Logger::Logger(const std::string &fileName, Logger::Level level, bool timestampEnabled)
{
    this->fileName.assign(fileName);
    this->currentLevel = level;
    this->timestampEnabled = timestampEnabled;
    this->printToStdout = false;

    this->logEnabled = true;
    this->f = NULL;

    pthread_mutex_init(&lock, NULL);
}

// ***********************************************/
// API

void Logger::setLevel(Logger::Level newLevel)
{
    currentLevel = newLevel;
}

Logger::Level Logger::getLevel() const
{
    return currentLevel;
}

void Logger::setFilename(const std::string& s)
{
    fileName.assign(s);
    if (f != NULL) fclose(f);
    f = NULL;
    enable();
}

const std::string& Logger::getFilename()
{
    return fileName;
}

void Logger::setTimestampFlag(bool flag)
{
    timestampEnabled = flag;
}

void Logger::setPrintToStdoutFlag(bool flag)
{
    printToStdout = flag;
}

void Logger::enable()
{
    logEnabled = true;
}

void Logger::disable()
{
    logEnabled = false;
}

static void prt_backtrace(FILE *fh)
{
#define BT_BUFSZ 50
	void *bt_buf[BT_BUFSZ];

	int stack_depth = backtrace(bt_buf, BT_BUFSZ);
	char **syms = backtrace_symbols(bt_buf, stack_depth);

	// Start printing at a bit into the stack, so as to avoid recording
	// the logger functions in the stack trace.
	fprintf(fh, "\tStack Trace:\n");
	for (int i=2; i< stack_depth; i++)
	{
		fprintf(fh, "\t%d: %s\n", i, syms[i]);
	}
	fprintf(fh, "\n");
	free(syms);
}

void Logger::log(Logger::Level level, const std::string &txt)
{
    if (!logEnabled) return;
    // delay opening the file until the first logging statement is issued;
    // this allows us to set the main logger's filename without creating
    // a useless log file with the default filename
    if (f == NULL) {
        if ((f = fopen(fileName.c_str(), "a")) == NULL) {
            fprintf(stderr, "[ERROR] Unable to open log file \"%s\"\n", fileName.c_str());
            disable();
            return;
        } else enable();
    }

    if (level <= currentLevel) {
        pthread_mutex_lock(&lock);
        if (timestampEnabled) {
            char timestamp[64];
            struct timeval stv;
            struct tm stm;

            ::gettimeofday(&stv, NULL);
            time_t t = stv.tv_sec;
            gmtime_r(&t, &stm);
            strftime(timestamp, sizeof(timestamp), "%F %T", &stm);
            fprintf(f, "[%s:%03ld] ", timestamp, stv.tv_usec / 1000);
            if (printToStdout) fprintf(stdout, "[%s:%03ld] ", timestamp, stv.tv_usec / 1000);
        }

        fprintf(f, "[%s] %s\n", getLevelString(level), txt.c_str());
        if (printToStdout) fprintf(stdout, "[%s] %s\n", getLevelString(level), txt.c_str());
        fflush(f);

        // Print a backtrace for warnings and errors.
        if (level <= WARN)
        {
            prt_backtrace(f);
        }
        fflush(f);
        pthread_mutex_unlock(&lock);
    }
}
void Logger::error(const std::string &txt)
{
    log(ERROR, txt);
}
void Logger::warn (const std::string &txt)
{
    log(WARN,  txt);
}
void Logger::info (const std::string &txt)
{
    log(INFO,  txt);
}
void Logger::debug(const std::string &txt)
{
    log(DEBUG, txt);
}
void Logger::fine (const std::string &txt)
{
    log(FINE,  txt);
}

void Logger::logva(Logger::Level level, const char *fmt, va_list args)
{
    if (level <= currentLevel) {
        char buffer[MAX_PRINTF_STYLE_MESSAGE_SIZE];
        vsnprintf(buffer, sizeof(buffer), fmt, args);
        std::string msg = buffer;
        log(level, msg);
    }
}

void Logger::log(Logger::Level level, const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(level, fmt, args); va_end(args);
}
void Logger::error(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(ERROR, fmt, args); va_end(args);
}
void Logger::warn (const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(WARN,  fmt, args); va_end(args);
}
void Logger::info (const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(INFO,  fmt, args); va_end(args);
}
void Logger::debug(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(DEBUG, fmt, args); va_end(args);
}
void Logger::fine (const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(FINE,  fmt, args); va_end(args);
}

const char* Logger::getLevelString(const Logger::Level level)
{
    return levelStrings[level];
}

const Logger::Level Logger::getLevelFromString(const std::string& levelStr)
{
    unsigned int nLevels = sizeof(levelStrings) / sizeof(levelStrings[0]);
    for (unsigned int i = 0; i < nLevels; ++i) {
        if (strcasecmp(levelStrings[i], levelStr.c_str()) == 0)
            return ((Logger::Level) i);
    }
    return ((Logger::Level) - 1);
}

// create and return the single instance
Logger& opencog::logger()
{
    static Logger instance;
    return instance;
}
<|endoftext|>


<commit_before>/*
 * opencog/util/Logger.cc
 *
 * Copyright (C) 2002-2007 Novamente LLC
 * Copyright (C) 2008 by OpenCog Foundation
 * Copyright (C) 2009, 2011 Linas Vepstas
 * Copyright (C) 2010 OpenCog Foundation
 * All Rights Reserved
 *
 * Written by Andre Senna <senna@vettalabs.com>
 *            Gustavo Gama <gama@vettalabs.com>
 *            Joel Pitt <joel@opencog.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "Logger.h"
#include "Config.h"

#include <pthread.h>
#define pthread_yield sched_yield

#ifndef WIN32
#include <cxxabi.h>
#include <execinfo.h>
#endif

#include <iostream>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <sstream>

#ifdef WIN32_NOT_UNIX
#include <winsock2.h>
#undef ERROR
#undef DEBUG
#else
#include <sys/time.h>
#endif

#include <boost/algorithm/string.hpp>

#include <opencog/util/platform.h>

using namespace opencog;

// messages greater than this will be truncated
#define MAX_PRINTF_STYLE_MESSAGE_SIZE (1<<15)
const char* levelStrings[] = {"NONE", "ERROR", "WARN", "INFO", "DEBUG", "FINE"};

#ifndef WIN32 /// @todo backtrace and backtrace_symbols is UNIX, we
              /// may need a WIN32 version
static void prt_backtrace(std::ostringstream& oss)
{
#define BT_BUFSZ 50
	void *bt_buf[BT_BUFSZ];

	int stack_depth = backtrace(bt_buf, BT_BUFSZ);
	char **syms = backtrace_symbols(bt_buf, stack_depth);

	// Start printing at a bit into the stack, so as to avoid recording
	// the logger functions in the stack trace.
	oss << "\tStack Trace:\n";
	for (int i=2; i < stack_depth; i++)
	{
		// Most things we'll print are mangled C++ names,
		// So demangle them, get them to pretty-print.
		char * begin = strchr(syms[i], '(');
		char * end = strchr(syms[i], '+');
		if (!(begin && end) || end <= begin)
		{
			// Failed to pull apart the symbol names
            oss << "\t" << i << ": " << syms[i] << "\n";
		}
		else
		{
			*begin = 0x0;
			oss << "\t" << i << ": " << syms[i] << " ";
			*begin = '(';
			size_t sz = 250;
			int status;
			char *fname = (char *) malloc(sz);
			*end = 0x0;
			char *rv = abi::__cxa_demangle(begin+1, fname, &sz, &status);
			*end = '+';
			if (rv) fname = rv; // might have re-alloced
			oss << "(" << fname << " " << end << std::endl;
			free(fname);
		}
	}
	oss << std::endl;
	free(syms);
}
#endif

Logger::~Logger()
{
#ifdef ASYNC_LOGGING
    // Wait for queue to empty
    flush();
    stopWriteLoop();
#endif

    if (f != NULL) fclose(f);
}

#ifdef ASYNC_LOGGING
void Logger::startWriteLoop()
{
    pthread_mutex_lock(&lock);
    if (!writingLoopActive)
    {
        writingLoopActive = true;
        m_Thread = boost::thread(&Logger::writingLoop, this);
    }
    pthread_mutex_unlock(&lock);
}

void Logger::stopWriteLoop()
{
    pthread_mutex_lock(&lock);
    pendingMessagesToWrite.cancel();
    // rejoin thread
    m_Thread.join();
    writingLoopActive = false;
    pthread_mutex_unlock(&lock);
}

void Logger::writingLoop()
{
    try
    {
        while (true)
        {
            // Must not pop until *after* the message has been written,
            // as otherwise, the flush() call will race with the write,
            // causing flush to report an empty queue, even though the
            // message has not actually been written yet.
            std::string* msg;
            pendingMessagesToWrite.wait_and_get(msg);
            writeMsg(*msg);
            pendingMessagesToWrite.pop();
            delete msg;
        }
    }
    catch (concurrent_queue< std::string* >::Canceled &e)
    {
        return;
    }
}

void Logger::flush()
{
    while (!pendingMessagesToWrite.empty())
    {
        pthread_yield();
        usleep(100);
    }
}
#endif

void Logger::writeMsg(std::string &msg)
{
    pthread_mutex_lock(&lock);
    // delay opening the file until the first logging statement is issued;
    // this allows us to set the main logger's filename without creating
    // a useless log file with the default filename
    if (f == NULL)
    {
        if ((f = fopen(fileName.c_str(), "a")) == NULL)
        {
            fprintf(stderr, "[ERROR] Unable to open log file \"%s\"\n",
                    fileName.c_str());
            pthread_mutex_unlock(&lock);
            disable();
            return;
        }
        else
            enable();
    }

    // write to file
    fprintf(f, "%s", msg.c_str());
    fflush(f);
    pthread_mutex_unlock(&lock);

    // write to stdout
    if (printToStdout)
    {
        std::cout << msg;
        std::cout.flush();
    }
}

Logger::Logger(const std::string &fname, Logger::Level level, bool tsEnabled)
    : error(*this), warn(*this), info(*this), debug(*this), fine(*this)
{
    this->fileName.assign(fname);
    this->currentLevel = level;
    this->backTraceLevel = getLevelFromString(opencog::config()["BACK_TRACE_LOG_LEVEL"]);
    this->timestampEnabled = tsEnabled;
    this->printToStdout = false;

    this->logEnabled = true;
    this->f = NULL;

    pthread_mutex_init(&lock, NULL);
#ifdef ASYNC_LOGGING
    this->writingLoopActive = false;
    startWriteLoop();
#endif // ASYNC_LOGGING
}

Logger::Logger(const Logger& log)
    : error(*this), warn(*this), info(*this), debug(*this), fine(*this)
{
    pthread_mutex_init(&lock, NULL);
    set(log);
}

Logger& Logger::operator=(const Logger& log)
{
#ifdef ASYNC_LOGGING
    this->stopWriteLoop();
    pendingMessagesToWrite.cancel_reset();
#endif // ASYNC_LOGGING
    this->set(log);
    return *this;
}

void Logger::set(const Logger& log)
{
    pthread_mutex_lock(&lock);
    this->fileName.assign(log.fileName);
    this->currentLevel = log.currentLevel;
    this->backTraceLevel = log.backTraceLevel;
    this->timestampEnabled = log.timestampEnabled;
    this->printToStdout = log.printToStdout;

    this->logEnabled = log.logEnabled;
    this->f = log.f;
    pthread_mutex_unlock(&lock);

#ifdef ASYNC_LOGGING
    startWriteLoop();
#endif // ASYNC_LOGGING
}

// ***********************************************/
// API

void Logger::setLevel(Logger::Level newLevel)
{
    currentLevel = newLevel;
}

Logger::Level Logger::getLevel() const
{
    return currentLevel;
}

void Logger::setBackTraceLevel(Logger::Level newLevel)
{
    backTraceLevel = newLevel;
}

Logger::Level Logger::getBackTraceLevel() const
{
    return backTraceLevel;
}

void Logger::setFilename(const std::string& s)
{
    fileName.assign(s);

    pthread_mutex_lock(&lock);
    if (f != NULL) fclose(f);
    f = NULL;
    pthread_mutex_unlock(&lock);

    enable();
}

const std::string& Logger::getFilename()
{
    return fileName;
}

void Logger::setTimestampFlag(bool flag)
{
    timestampEnabled = flag;
}

void Logger::setPrintToStdoutFlag(bool flag)
{
    printToStdout = flag;
}

void Logger::setPrintErrorLevelStdout() {
    setPrintToStdoutFlag(true);
    setLevel(Logger::ERROR);
}

void Logger::enable()
{
    logEnabled = true;
}

void Logger::disable()
{
    logEnabled = false;
}

void Logger::log(Logger::Level level, const std::string &txt)
{
#ifdef ASYNC_LOGGING
    static const unsigned int max_queue_size_allowed = 1024;
#endif
    static char timestamp[64];
    static char timestampStr[256];

    // Don't log if not enabled, or level is too low.
    if (!logEnabled) return;
    if (level > currentLevel) return;

    std::ostringstream oss;
    if (timestampEnabled)
    {
        struct timeval stv;
        struct tm stm;

        ::gettimeofday(&stv, NULL);
        time_t t = stv.tv_sec;
        gmtime_r(&t, &stm);
        strftime(timestamp, sizeof(timestamp), "%F %T", &stm);
        snprintf(timestampStr, sizeof(timestampStr),
                "[%s:%03ld] ",timestamp, stv.tv_usec / 1000);
        oss << timestampStr;
    }

    oss << "[" << getLevelString(level) << "] " << txt << std::endl;

    if (level <= backTraceLevel)
    {
#ifndef WIN32
        prt_backtrace(oss);
#endif
    }
#ifdef ASYNC_LOGGING
    pendingMessagesToWrite.push(new std::string(oss.str()));

    // If the queue gets too full, block until it's flushed to file or
    // stdout
    if (pendingMessagesToWrite.approx_size() > max_queue_size_allowed) {
        flush();
    }
#else
    std::string temp(oss.str());
    writeMsg(temp);
#endif
}

void Logger::logva(Logger::Level level, const char *fmt, va_list args)
{
    if (level <= currentLevel) {
        char buffer[MAX_PRINTF_STYLE_MESSAGE_SIZE];
        vsnprintf(buffer, sizeof(buffer), fmt, args);
        std::string msg = buffer;
        log(level, msg);
    }
}

void Logger::log(Logger::Level level, const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(level, fmt, args); va_end(args);
}
void Logger::Error::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(ERROR, fmt, args); va_end(args);
}
void Logger::Warn::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(WARN,  fmt, args); va_end(args);
}
void Logger::Info::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(INFO,  fmt, args); va_end(args);
}
void Logger::Debug::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(DEBUG, fmt, args); va_end(args);
}
void Logger::Fine::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(FINE,  fmt, args); va_end(args);
}

bool Logger::isEnabled(Level level) const
{
    return level <= currentLevel;
}

bool Logger::isErrorEnabled() const
{
    return ERROR <= currentLevel;
}

bool Logger::isWarnEnabled() const
{
    return WARN <= currentLevel;
}

bool Logger::isInfoEnabled() const
{
    return INFO <= currentLevel;
}

bool Logger::isDebugEnabled() const
{
    return DEBUG <= currentLevel;
}

bool Logger::isFineEnabled() const
{
    return FINE <= currentLevel;
}

const char* Logger::getLevelString(const Logger::Level level)
{
    if (level == BAD_LEVEL)
        return "Bad level";
    else
        return levelStrings[level];
}

const Logger::Level Logger::getLevelFromString(const std::string& levelStr)
{
    unsigned int nLevels = sizeof(levelStrings) / sizeof(levelStrings[0]);
    for (unsigned int i = 0; i < nLevels; ++i) {
        if (boost::iequals(levelStrings[i], levelStr))
            return (Logger::Level) i;
    }
    return BAD_LEVEL;
}

// create and return the single instance
Logger& opencog::logger()
{
    static Logger instance;
    return instance;
}
<commit_msg>logger: if we are going to do this OSX thing, lets do it correctly.<commit_after>/*
 * opencog/util/Logger.cc
 *
 * Copyright (C) 2002-2007 Novamente LLC
 * Copyright (C) 2008 by OpenCog Foundation
 * Copyright (C) 2009, 2011 Linas Vepstas
 * Copyright (C) 2010 OpenCog Foundation
 * All Rights Reserved
 *
 * Written by Andre Senna <senna@vettalabs.com>
 *            Gustavo Gama <gama@vettalabs.com>
 *            Joel Pitt <joel@opencog.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "Logger.h"
#include "Config.h"

#ifndef WIN32
#include <cxxabi.h>
#include <execinfo.h>
#endif

#include <iostream>
#include <sstream>

#include <pthread.h>
#include <sched.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>

#ifdef WIN32_NOT_UNIX
#include <winsock2.h>
#undef ERROR
#undef DEBUG
#else
#include <sys/time.h>
#endif

#include <boost/algorithm/string.hpp>

#include <opencog/util/platform.h>

using namespace opencog;

// messages greater than this will be truncated
#define MAX_PRINTF_STYLE_MESSAGE_SIZE (1<<15)
const char* levelStrings[] = {"NONE", "ERROR", "WARN", "INFO", "DEBUG", "FINE"};

#ifndef WIN32 /// @todo backtrace and backtrace_symbols is UNIX, we
              /// may need a WIN32 version
static void prt_backtrace(std::ostringstream& oss)
{
#define BT_BUFSZ 50
	void *bt_buf[BT_BUFSZ];

	int stack_depth = backtrace(bt_buf, BT_BUFSZ);
	char **syms = backtrace_symbols(bt_buf, stack_depth);

	// Start printing at a bit into the stack, so as to avoid recording
	// the logger functions in the stack trace.
	oss << "\tStack Trace:\n";
	for (int i=2; i < stack_depth; i++)
	{
		// Most things we'll print are mangled C++ names,
		// So demangle them, get them to pretty-print.
		char * begin = strchr(syms[i], '(');
		char * end = strchr(syms[i], '+');
		if (!(begin && end) || end <= begin)
		{
			// Failed to pull apart the symbol names
            oss << "\t" << i << ": " << syms[i] << "\n";
		}
		else
		{
			*begin = 0x0;
			oss << "\t" << i << ": " << syms[i] << " ";
			*begin = '(';
			size_t sz = 250;
			int status;
			char *fname = (char *) malloc(sz);
			*end = 0x0;
			char *rv = abi::__cxa_demangle(begin+1, fname, &sz, &status);
			*end = '+';
			if (rv) fname = rv; // might have re-alloced
			oss << "(" << fname << " " << end << std::endl;
			free(fname);
		}
	}
	oss << std::endl;
	free(syms);
}
#endif

Logger::~Logger()
{
#ifdef ASYNC_LOGGING
    // Wait for queue to empty
    flush();
    stopWriteLoop();
#endif

    if (f != NULL) fclose(f);
}

#ifdef ASYNC_LOGGING
void Logger::startWriteLoop()
{
    pthread_mutex_lock(&lock);
    if (!writingLoopActive)
    {
        writingLoopActive = true;
        m_Thread = boost::thread(&Logger::writingLoop, this);
    }
    pthread_mutex_unlock(&lock);
}

void Logger::stopWriteLoop()
{
    pthread_mutex_lock(&lock);
    pendingMessagesToWrite.cancel();
    // rejoin thread
    m_Thread.join();
    writingLoopActive = false;
    pthread_mutex_unlock(&lock);
}

void Logger::writingLoop()
{
    try
    {
        while (true)
        {
            // Must not pop until *after* the message has been written,
            // as otherwise, the flush() call will race with the write,
            // causing flush to report an empty queue, even though the
            // message has not actually been written yet.
            std::string* msg;
            pendingMessagesToWrite.wait_and_get(msg);
            writeMsg(*msg);
            pendingMessagesToWrite.pop();
            delete msg;
        }
    }
    catch (concurrent_queue< std::string* >::Canceled &e)
    {
        return;
    }
}

void Logger::flush()
{
    while (!pendingMessagesToWrite.empty())
    {
        sched_yield();
        usleep(100);
    }
}
#endif

void Logger::writeMsg(std::string &msg)
{
    pthread_mutex_lock(&lock);
    // delay opening the file until the first logging statement is issued;
    // this allows us to set the main logger's filename without creating
    // a useless log file with the default filename
    if (f == NULL)
    {
        if ((f = fopen(fileName.c_str(), "a")) == NULL)
        {
            fprintf(stderr, "[ERROR] Unable to open log file \"%s\"\n",
                    fileName.c_str());
            pthread_mutex_unlock(&lock);
            disable();
            return;
        }
        else
            enable();
    }

    // write to file
    fprintf(f, "%s", msg.c_str());
    fflush(f);
    pthread_mutex_unlock(&lock);

    // write to stdout
    if (printToStdout)
    {
        std::cout << msg;
        std::cout.flush();
    }
}

Logger::Logger(const std::string &fname, Logger::Level level, bool tsEnabled)
    : error(*this), warn(*this), info(*this), debug(*this), fine(*this)
{
    this->fileName.assign(fname);
    this->currentLevel = level;
    this->backTraceLevel = getLevelFromString(opencog::config()["BACK_TRACE_LOG_LEVEL"]);
    this->timestampEnabled = tsEnabled;
    this->printToStdout = false;

    this->logEnabled = true;
    this->f = NULL;

    pthread_mutex_init(&lock, NULL);
#ifdef ASYNC_LOGGING
    this->writingLoopActive = false;
    startWriteLoop();
#endif // ASYNC_LOGGING
}

Logger::Logger(const Logger& log)
    : error(*this), warn(*this), info(*this), debug(*this), fine(*this)
{
    pthread_mutex_init(&lock, NULL);
    set(log);
}

Logger& Logger::operator=(const Logger& log)
{
#ifdef ASYNC_LOGGING
    this->stopWriteLoop();
    pendingMessagesToWrite.cancel_reset();
#endif // ASYNC_LOGGING
    this->set(log);
    return *this;
}

void Logger::set(const Logger& log)
{
    pthread_mutex_lock(&lock);
    this->fileName.assign(log.fileName);
    this->currentLevel = log.currentLevel;
    this->backTraceLevel = log.backTraceLevel;
    this->timestampEnabled = log.timestampEnabled;
    this->printToStdout = log.printToStdout;

    this->logEnabled = log.logEnabled;
    this->f = log.f;
    pthread_mutex_unlock(&lock);

#ifdef ASYNC_LOGGING
    startWriteLoop();
#endif // ASYNC_LOGGING
}

// ***********************************************/
// API

void Logger::setLevel(Logger::Level newLevel)
{
    currentLevel = newLevel;
}

Logger::Level Logger::getLevel() const
{
    return currentLevel;
}

void Logger::setBackTraceLevel(Logger::Level newLevel)
{
    backTraceLevel = newLevel;
}

Logger::Level Logger::getBackTraceLevel() const
{
    return backTraceLevel;
}

void Logger::setFilename(const std::string& s)
{
    fileName.assign(s);

    pthread_mutex_lock(&lock);
    if (f != NULL) fclose(f);
    f = NULL;
    pthread_mutex_unlock(&lock);

    enable();
}

const std::string& Logger::getFilename()
{
    return fileName;
}

void Logger::setTimestampFlag(bool flag)
{
    timestampEnabled = flag;
}

void Logger::setPrintToStdoutFlag(bool flag)
{
    printToStdout = flag;
}

void Logger::setPrintErrorLevelStdout() {
    setPrintToStdoutFlag(true);
    setLevel(Logger::ERROR);
}

void Logger::enable()
{
    logEnabled = true;
}

void Logger::disable()
{
    logEnabled = false;
}

void Logger::log(Logger::Level level, const std::string &txt)
{
#ifdef ASYNC_LOGGING
    static const unsigned int max_queue_size_allowed = 1024;
#endif
    static char timestamp[64];
    static char timestampStr[256];

    // Don't log if not enabled, or level is too low.
    if (!logEnabled) return;
    if (level > currentLevel) return;

    std::ostringstream oss;
    if (timestampEnabled)
    {
        struct timeval stv;
        struct tm stm;

        ::gettimeofday(&stv, NULL);
        time_t t = stv.tv_sec;
        gmtime_r(&t, &stm);
        strftime(timestamp, sizeof(timestamp), "%F %T", &stm);
        snprintf(timestampStr, sizeof(timestampStr),
                "[%s:%03ld] ",timestamp, stv.tv_usec / 1000);
        oss << timestampStr;
    }

    oss << "[" << getLevelString(level) << "] " << txt << std::endl;

    if (level <= backTraceLevel)
    {
#ifndef WIN32
        prt_backtrace(oss);
#endif
    }
#ifdef ASYNC_LOGGING
    pendingMessagesToWrite.push(new std::string(oss.str()));

    // If the queue gets too full, block until it's flushed to file or
    // stdout
    if (pendingMessagesToWrite.approx_size() > max_queue_size_allowed) {
        flush();
    }
#else
    std::string temp(oss.str());
    writeMsg(temp);
#endif
}

void Logger::logva(Logger::Level level, const char *fmt, va_list args)
{
    if (level <= currentLevel) {
        char buffer[MAX_PRINTF_STYLE_MESSAGE_SIZE];
        vsnprintf(buffer, sizeof(buffer), fmt, args);
        std::string msg = buffer;
        log(level, msg);
    }
}

void Logger::log(Logger::Level level, const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logva(level, fmt, args); va_end(args);
}
void Logger::Error::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(ERROR, fmt, args); va_end(args);
}
void Logger::Warn::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(WARN,  fmt, args); va_end(args);
}
void Logger::Info::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(INFO,  fmt, args); va_end(args);
}
void Logger::Debug::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(DEBUG, fmt, args); va_end(args);
}
void Logger::Fine::operator()(const char *fmt, ...)
{
    va_list args; va_start(args, fmt); logger.logva(FINE,  fmt, args); va_end(args);
}

bool Logger::isEnabled(Level level) const
{
    return level <= currentLevel;
}

bool Logger::isErrorEnabled() const
{
    return ERROR <= currentLevel;
}

bool Logger::isWarnEnabled() const
{
    return WARN <= currentLevel;
}

bool Logger::isInfoEnabled() const
{
    return INFO <= currentLevel;
}

bool Logger::isDebugEnabled() const
{
    return DEBUG <= currentLevel;
}

bool Logger::isFineEnabled() const
{
    return FINE <= currentLevel;
}

const char* Logger::getLevelString(const Logger::Level level)
{
    if (level == BAD_LEVEL)
        return "Bad level";
    else
        return levelStrings[level];
}

const Logger::Level Logger::getLevelFromString(const std::string& levelStr)
{
    unsigned int nLevels = sizeof(levelStrings) / sizeof(levelStrings[0]);
    for (unsigned int i = 0; i < nLevels; ++i) {
        if (boost::iequals(levelStrings[i], levelStr))
            return (Logger::Level) i;
    }
    return BAD_LEVEL;
}

// create and return the single instance
Logger& opencog::logger()
{
    static Logger instance;
    return instance;
}
<|endoftext|>


<commit_before>/************************************************************************************
*                                                                                *
*   Copyright (c) 2014, 2015 - 2016 Axel Menzel <info@rttr.org>                     *
*                                                                                *
*   This file is part of RTTR (Run Time Type Reflection)                            *
*   License: MIT License                                                            *
*                                                                                *
*   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.                                                                       *
*                                                                                *
*************************************************************************************/

#include "unit_tests/variant/test_enums.h"

#include <catch/catch.hpp>
#include <rttr/registration>

using namespace rttr;

RTTR_REGISTRATION
{
    registration::enumeration<enum_int64_t>("enum_int64_t")
    (
        value("VALUE_1", enum_int64_t::VALUE_1),
        value("VALUE_2", enum_int64_t::VALUE_2),
        value("VALUE_3", enum_int64_t::VALUE_3),
        value("VALUE_4", enum_int64_t::VALUE_4),
        value("VALUE_X", enum_int64_t::VALUE_NEG)
    )
    ;
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from empty", "[variant]")
{
    variant var;
    bool ok = false;
    CHECK(var.to_string(&ok) == "");
    CHECK(ok == false);
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from bool", "[variant]")
{
    variant var = true;
    REQUIRE(var.is_valid() == true);
    REQUIRE(var.can_convert<std::string>() == true);

    // true case
    bool ok = false;
    CHECK(var.to_string(&ok) == "true");
    CHECK(ok == true);

    CHECK(var.convert<std::string>(&ok) == "true");
    CHECK(ok == true);
    REQUIRE(var.convert(type::get<std::string>()) == true);
    CHECK(var.get_value<std::string>() == "true");

    // false case
    var = false;
    CHECK(var.to_string(&ok) == "false");
    CHECK(ok == true);

    CHECK(var.convert<std::string>(&ok) == "false");
    CHECK(ok == true);
    REQUIRE(var.convert(type::get<std::string>()) == true);
    CHECK(var.get_value<std::string>() == "false");
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from char", "[variant]")
{
    SECTION("valid conversion")
    {
        variant var = char('A');
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "A");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "A");
    }

    SECTION("valid conversion negative")
    {
        variant var = char(-60);
        bool ok = false;
        CHECK(var.to_string(&ok) == "");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from std::string", "[variant]")
{
    variant var = std::string("23");
    REQUIRE(var.can_convert<std::string>() == true);
    bool ok = false;
    CHECK(var.to_string(&ok) == "23");
    CHECK(ok == true);

    var = std::string("-12");
    CHECK(var.to_string() == "-12");
    REQUIRE(var.convert(type::get<std::string>()) == true);
    CHECK(var.get_value<std::string>() == "-12");
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int", "[variant]")
{
    SECTION("conversion positive")
    {
        variant var = 2147483640;
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "2147483640");

        CHECK(ok == true);
        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "2147483640");
    }

    SECTION("conversion negative")
    {
        variant var = -2147483640;
        bool ok = false;
        CHECK(var.to_string(&ok) == "-2147483640");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from float", "[variant]")
{
    SECTION("conversion positive")
    {
        variant var = 214748.9f;
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "214749");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "214749");
    }

    SECTION("conversion negative")
    {
        variant var = -214748.9f;
        bool ok = false;
        CHECK(var.to_string(&ok) == "-214749");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from double", "[variant]")
{
    SECTION("conversion positive")
    {
        variant var = 5000000000.9;
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "5000000000.9");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "5000000000.9");
    }

    SECTION("conversion negative")
    {
        variant var = -5000000000.9;
        bool ok = false;
        CHECK(var.to_string(&ok) == "-5000000000.9");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int8_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = int8_t(50);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "50");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "50");
    }

    SECTION("valid conversion negative")
    {
        variant var = int8_t(-60);
        bool ok = false;
        CHECK(var.to_string(&ok) == "-60");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int16_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = int16_t(32760);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "32760");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "32760");
    }

    SECTION("valid conversion negative")
    {
        variant var = int16_t(-32760);
        bool ok = false;
        CHECK(var.to_string(&ok) == "-32760");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int32_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = int32_t(2147483640);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "2147483640");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "2147483640");
    }

    SECTION("valid conversion negative")
    {
        variant var = int32_t(-2147483640);
        bool ok = false;
        CHECK(var.to_string(&ok) == "-2147483640");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int64_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = int64_t(9023372036854775807L);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "9023372036854775807");
        CHECK(ok == true);

        CHECK(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "9023372036854775807");
    }

    SECTION("valid conversion negative")
    {
        variant var = int64_t(-9023372036854775808L);
        bool ok = false;
        CHECK(var.to_string(&ok) == "-9023372036854775808");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from uint8_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = uint8_t(50);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "50");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "50");
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from uint16_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = uint16_t(32760);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "32760");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "32760");
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from uint32_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = uint32_t(32760);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "32760");
        CHECK(ok == true);

        CHECK(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "32760");
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from uint64_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = uint64_t(2147483640);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "2147483640");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "2147483640");
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

enum class unregisterd_enum { VALUE_1 };

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from enum", "[variant]")
{
    SECTION("valid conversion")
    {
        variant var = enum_int64_t::VALUE_1;
        REQUIRE(var.can_convert<int64_t>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "VALUE_1");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "VALUE_1");
    }

    SECTION("invalid conversion")
    {
        variant var = unregisterd_enum::VALUE_1;
        bool ok = false;
        CHECK(var.to_string(&ok) == "");
        CHECK(ok == false);
        CHECK(var.convert(type::get<std::string>()) == false);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////
<commit_msg>fixed compiler warning with clang: 'illegal character encoding in string literal [-Winvalid-source-encoding]'<commit_after>/************************************************************************************
*                                                                                *
*   Copyright (c) 2014, 2015 - 2016 Axel Menzel <info@rttr.org>                     *
*                                                                                *
*   This file is part of RTTR (Run Time Type Reflection)                            *
*   License: MIT License                                                            *
*                                                                                *
*   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.                                                                       *
*                                                                                *
*************************************************************************************/

#include "unit_tests/variant/test_enums.h"

#include <catch/catch.hpp>
#include <rttr/registration>

using namespace rttr;

RTTR_REGISTRATION
{
    registration::enumeration<enum_int64_t>("enum_int64_t")
    (
        value("VALUE_1", enum_int64_t::VALUE_1),
        value("VALUE_2", enum_int64_t::VALUE_2),
        value("VALUE_3", enum_int64_t::VALUE_3),
        value("VALUE_4", enum_int64_t::VALUE_4),
        value("VALUE_X", enum_int64_t::VALUE_NEG)
    )
    ;
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from empty", "[variant]")
{
    variant var;
    bool ok = false;
    CHECK(var.to_string(&ok) == "");
    CHECK(ok == false);
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from bool", "[variant]")
{
    variant var = true;
    REQUIRE(var.is_valid() == true);
    REQUIRE(var.can_convert<std::string>() == true);

    // true case
    bool ok = false;
    CHECK(var.to_string(&ok) == "true");
    CHECK(ok == true);

    CHECK(var.convert<std::string>(&ok) == "true");
    CHECK(ok == true);
    REQUIRE(var.convert(type::get<std::string>()) == true);
    CHECK(var.get_value<std::string>() == "true");

    // false case
    var = false;
    CHECK(var.to_string(&ok) == "false");
    CHECK(ok == true);

    CHECK(var.convert<std::string>(&ok) == "false");
    CHECK(ok == true);
    REQUIRE(var.convert(type::get<std::string>()) == true);
    CHECK(var.get_value<std::string>() == "false");
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from char", "[variant]")
{
    SECTION("valid conversion")
    {
        variant var = char('A');
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "A");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "A");
    }

    SECTION("valid conversion negative")
    {
        variant var = char(-60);
        bool ok = false;
        CHECK(var.to_string(&ok) == std::string(1, char(-60)));
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from std::string", "[variant]")
{
    variant var = std::string("23");
    REQUIRE(var.can_convert<std::string>() == true);
    bool ok = false;
    CHECK(var.to_string(&ok) == "23");
    CHECK(ok == true);

    var = std::string("-12");
    CHECK(var.to_string() == "-12");
    REQUIRE(var.convert(type::get<std::string>()) == true);
    CHECK(var.get_value<std::string>() == "-12");
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int", "[variant]")
{
    SECTION("conversion positive")
    {
        variant var = 2147483640;
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "2147483640");

        CHECK(ok == true);
        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "2147483640");
    }

    SECTION("conversion negative")
    {
        variant var = -2147483640;
        bool ok = false;
        CHECK(var.to_string(&ok) == "-2147483640");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from float", "[variant]")
{
    SECTION("conversion positive")
    {
        variant var = 214748.9f;
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "214749");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "214749");
    }

    SECTION("conversion negative")
    {
        variant var = -214748.9f;
        bool ok = false;
        CHECK(var.to_string(&ok) == "-214749");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from double", "[variant]")
{
    SECTION("conversion positive")
    {
        variant var = 5000000000.9;
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "5000000000.9");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "5000000000.9");
    }

    SECTION("conversion negative")
    {
        variant var = -5000000000.9;
        bool ok = false;
        CHECK(var.to_string(&ok) == "-5000000000.9");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int8_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = int8_t(50);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "50");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "50");
    }

    SECTION("valid conversion negative")
    {
        variant var = int8_t(-60);
        bool ok = false;
        CHECK(var.to_string(&ok) == "-60");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int16_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = int16_t(32760);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "32760");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "32760");
    }

    SECTION("valid conversion negative")
    {
        variant var = int16_t(-32760);
        bool ok = false;
        CHECK(var.to_string(&ok) == "-32760");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int32_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = int32_t(2147483640);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "2147483640");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "2147483640");
    }

    SECTION("valid conversion negative")
    {
        variant var = int32_t(-2147483640);
        bool ok = false;
        CHECK(var.to_string(&ok) == "-2147483640");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from int64_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = int64_t(9023372036854775807L);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "9023372036854775807");
        CHECK(ok == true);

        CHECK(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "9023372036854775807");
    }

    SECTION("valid conversion negative")
    {
        variant var = int64_t(-9023372036854775808L);
        bool ok = false;
        CHECK(var.to_string(&ok) == "-9023372036854775808");
        CHECK(ok == true);
        CHECK(var.convert(type::get<std::string>()) == true);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from uint8_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = uint8_t(50);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "50");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "50");
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from uint16_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = uint16_t(32760);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "32760");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "32760");
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from uint32_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = uint32_t(32760);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "32760");
        CHECK(ok == true);

        CHECK(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "32760");
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from uint64_t", "[variant]")
{
    SECTION("valid conversion positive")
    {
        variant var = uint64_t(2147483640);
        REQUIRE(var.can_convert<std::string>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "2147483640");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "2147483640");
    }
}

/////////////////////////////////////////////////////////////////////////////////////////

enum class unregisterd_enum { VALUE_1 };

/////////////////////////////////////////////////////////////////////////////////////////

TEST_CASE("variant::to_string() - from enum", "[variant]")
{
    SECTION("valid conversion")
    {
        variant var = enum_int64_t::VALUE_1;
        REQUIRE(var.can_convert<int64_t>() == true);
        bool ok = false;
        CHECK(var.to_string(&ok) == "VALUE_1");
        CHECK(ok == true);

        REQUIRE(var.convert(type::get<std::string>()) == true);
        CHECK(var.get_value<std::string>() == "VALUE_1");
    }

    SECTION("invalid conversion")
    {
        variant var = unregisterd_enum::VALUE_1;
        bool ok = false;
        CHECK(var.to_string(&ok) == "");
        CHECK(ok == false);
        CHECK(var.convert(type::get<std::string>()) == false);
    }
}

/////////////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

/* A stub for Windows console processes (like nmake) that is able to terminate
 * its child process via a generated Ctrl-C event.
 * The termination is triggered by sending a custom message to the HWND of
 * this process. */

#ifndef WINVER
#define WINVER 0x0501
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#include <wchar.h>
#include <cstdlib>
#include <cstdio>

const wchar_t szTitle[] = L"qtcbuildhelper";
const wchar_t szWindowClass[] = L"wcqtcbuildhelper";
UINT uiShutDownWindowMessage;
HWND hwndMain = 0;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL WINAPI ctrlHandler(DWORD dwCtrlType);
bool findFirst(const wchar_t *str, const size_t strLength, const size_t startPos, const wchar_t *chars, size_t &pos);
bool startProcess(wchar_t pCommandLine[]);

int main(int argc, char **)
{
    if (argc < 2) {
        fprintf(stderr, "This is an internal helper of Qt Creator. Do not run it manually.\n");
        return 1;
    }

    SetConsoleCtrlHandler(ctrlHandler, TRUE);
    uiShutDownWindowMessage = RegisterWindowMessage(L"qtcbuildhelper_shutdown");

    WNDCLASSEX wcex;
    ZeroMemory(&wcex, sizeof(wcex));
    wcex.cbSize = sizeof(wcex);
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = GetModuleHandle(0);
    wcex.lpszClassName = szWindowClass;
    if (!RegisterClassEx(&wcex))
        return 1;

    hwndMain = CreateWindow(szWindowClass, szTitle, WS_DISABLED,
                            0, 0, 0, 0, NULL, NULL, wcex.hInstance, NULL);
    if (!hwndMain)
        return FALSE;

    // Get the command line and remove the call to this executable.
    // Note: We trust Qt Creator at this point to quote the call to this tool in a sensible way.
    // Strange things like C:\Q"t Crea"tor\bin\qtcbuildhelper.exe are not supported.
    wchar_t *strCommandLine = _wcsdup(GetCommandLine());
    const size_t strCommandLineLength = wcslen(strCommandLine);
    size_t pos = 1;
    if (strCommandLine[0] == L'"')
        if (!findFirst(strCommandLine, strCommandLineLength, pos, L"\"", pos))
            return 1;
    if (!findFirst(strCommandLine, strCommandLineLength, pos, L" \t", pos))
        return 1;
    wmemmove_s(strCommandLine, strCommandLineLength, strCommandLine + pos + 2, strCommandLineLength - pos);

    bool bSuccess = startProcess(strCommandLine);
    free(strCommandLine);

    if (!bSuccess)
        return 1;

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == uiShutDownWindowMessage) {
        GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
        PostQuitMessage(0);
        return 0;
    }

    switch (message)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

bool findFirst(const wchar_t *str, const size_t strLength, const size_t startPos, const wchar_t *chars, size_t &pos)
{
    for (size_t i=startPos; i < strLength; ++i) {
        for (size_t j=0; chars[j]; ++j) {
            if (str[i] == chars[j]) {
                pos = i;
                return true;
            }
        }
    }
    return false;
}

BOOL WINAPI ctrlHandler(DWORD /*dwCtrlType*/)
{
    PostMessage(hwndMain, WM_DESTROY, 0, 0);
    return TRUE;
}

DWORD WINAPI processWatcherThread(__in LPVOID lpParameter)
{
    HANDLE hProcess = reinterpret_cast<HANDLE>(lpParameter);
    WaitForSingleObject(hProcess, INFINITE);
    PostMessage(hwndMain, WM_DESTROY, 0, 0);
    return 0;
}

bool startProcess(wchar_t *pCommandLine)
{
    SECURITY_ATTRIBUTES sa;
    ZeroMemory(&sa, sizeof(sa));
    sa.nLength = sizeof(sa);
    sa.bInheritHandle = TRUE;

    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);

    PROCESS_INFORMATION pi;
    DWORD dwCreationFlags = 0;
    BOOL bSuccess = CreateProcess(NULL, pCommandLine, &sa, &sa, TRUE, dwCreationFlags, NULL, NULL, &si, &pi);
    if (!bSuccess) {
        fwprintf(stderr, L"qtcbuildhelper: Command line failed: %s\n", pCommandLine);
        return false;
    }

    HANDLE hThread = CreateThread(NULL, 0, processWatcherThread, reinterpret_cast<void*>(pi.hProcess), 0, NULL);
    if (!hThread) {
        fwprintf(stderr, L"qtcbuildhelper: The watch dog thread cannot be started.\n");
        return false;
    }
    CloseHandle(hThread);
    return true;
}

<commit_msg>fixup for qtcbuildhelper usage (change Ib6f5be80)<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

/* A stub for Windows console processes (like nmake) that is able to terminate
 * its child process via a generated Ctrl-C event.
 * The termination is triggered by sending a custom message to the HWND of
 * this process. */

#ifndef WINVER
#define WINVER 0x0501
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
#include <wchar.h>
#include <cstdlib>
#include <cstdio>

const wchar_t szTitle[] = L"qtcbuildhelper";
const wchar_t szWindowClass[] = L"wcqtcbuildhelper";
UINT uiShutDownWindowMessage;
HWND hwndMain = 0;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL WINAPI ctrlHandler(DWORD dwCtrlType);
bool findFirst(const wchar_t *str, const size_t strLength, const size_t startPos, const wchar_t *chars, size_t &pos);
bool startProcess(wchar_t pCommandLine[]);

int main(int argc, char **)
{
    if (argc < 2) {
        fprintf(stderr, "This is an internal helper of Qt Creator. Do not run it manually.\n");
        return 1;
    }

    SetConsoleCtrlHandler(ctrlHandler, TRUE);
    uiShutDownWindowMessage = RegisterWindowMessage(L"qtcbuildhelper_shutdown");

    WNDCLASSEX wcex;
    ZeroMemory(&wcex, sizeof(wcex));
    wcex.cbSize = sizeof(wcex);
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = GetModuleHandle(0);
    wcex.lpszClassName = szWindowClass;
    if (!RegisterClassEx(&wcex))
        return 1;

    hwndMain = CreateWindow(szWindowClass, szTitle, WS_DISABLED,
                            0, 0, 0, 0, NULL, NULL, wcex.hInstance, NULL);
    if (!hwndMain)
        return FALSE;

    // Get the command line and remove the call to this executable.
    // Note: We trust Qt Creator at this point to quote the call to this tool in a sensible way.
    // Strange things like C:\Q"t Crea"tor\bin\qtcbuildhelper.exe are not supported.
    wchar_t *strCommandLine = _wcsdup(GetCommandLine());
    const size_t strCommandLineLength = wcslen(strCommandLine);
    size_t pos = 1;
    if (strCommandLine[0] == L'"')
        if (!findFirst(strCommandLine, strCommandLineLength, pos, L"\"", pos))
            return 1;
    if (!findFirst(strCommandLine, strCommandLineLength, pos, L" \t", pos))
        return 1;
    wmemmove_s(strCommandLine, strCommandLineLength, strCommandLine + pos + 1, strCommandLineLength - pos);

    bool bSuccess = startProcess(strCommandLine);
    free(strCommandLine);

    if (!bSuccess)
        return 1;

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == uiShutDownWindowMessage) {
        GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
        PostQuitMessage(0);
        return 0;
    }

    switch (message)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

bool findFirst(const wchar_t *str, const size_t strLength, const size_t startPos, const wchar_t *chars, size_t &pos)
{
    for (size_t i=startPos; i < strLength; ++i) {
        for (size_t j=0; chars[j]; ++j) {
            if (str[i] == chars[j]) {
                pos = i;
                return true;
            }
        }
    }
    return false;
}

BOOL WINAPI ctrlHandler(DWORD /*dwCtrlType*/)
{
    PostMessage(hwndMain, WM_DESTROY, 0, 0);
    return TRUE;
}

DWORD WINAPI processWatcherThread(__in LPVOID lpParameter)
{
    HANDLE hProcess = reinterpret_cast<HANDLE>(lpParameter);
    WaitForSingleObject(hProcess, INFINITE);
    PostMessage(hwndMain, WM_DESTROY, 0, 0);
    return 0;
}

bool startProcess(wchar_t *pCommandLine)
{
    SECURITY_ATTRIBUTES sa;
    ZeroMemory(&sa, sizeof(sa));
    sa.nLength = sizeof(sa);
    sa.bInheritHandle = TRUE;

    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);

    PROCESS_INFORMATION pi;
    DWORD dwCreationFlags = 0;
    BOOL bSuccess = CreateProcess(NULL, pCommandLine, &sa, &sa, TRUE, dwCreationFlags, NULL, NULL, &si, &pi);
    if (!bSuccess) {
        fwprintf(stderr, L"qtcbuildhelper: Command line failed: %s\n", pCommandLine);
        return false;
    }

    HANDLE hThread = CreateThread(NULL, 0, processWatcherThread, reinterpret_cast<void*>(pi.hProcess), 0, NULL);
    if (!hThread) {
        fwprintf(stderr, L"qtcbuildhelper: The watch dog thread cannot be started.\n");
        return false;
    }
    CloseHandle(hThread);
    return true;
}

<|endoftext|>