<commit_before>/** @file
    @brief OSVR driver provider

    @date 2015

    @author
    Sensics, Inc.
    <http://sensics.com>

*/

// Copyright 2015 Sensics, Inc.
//
// All rights reserved.
//
// (Final version intended to be licensed under
// the Apache License, Version 2.0)

// Internal Includes
#include "ihmddriverprovider.h"
#include "ihmddriver.h"
#include "steamvr.h"

#include "osvr_compiler_detection.h"
#include "osvr_hmd.h"

#include "stringhasprefix.h"
#include "osvr_dll_export.h"

// Library/third-party includes
#include <osvr/ClientKit/Context.h>
#include <osvr/ClientKit/Interface.h>

// Standard includes
#include <vector>
#include <cstring>

class CDriver_OSVR : public vr::IHmdDriverProvider {
public:
	/**
	 * Initializes the driver.
	 *
	 * This is called when the driver is first loaded.
	 *
	 * @param user_config_dir the absoluate path of the directory where the
	 *     driver should store any user configuration files.
	 * @param driver_install_dir the absolute path of the driver's root
	 *     directory.
	 *
	 * If Init() returns anything other than \c HmdError_None the driver will be
	 * unloaded.
	 *
	 * @returns HmdError_None on success.
	 */
	virtual vr::HmdError Init(const char* pchUserConfigDir, const char* pchDriverInstallDir) OSVR_OVERRIDE;

	/**
	 * Performs any cleanup prior to the driver being unloaded.
	 */
	virtual void Cleanup() OSVR_OVERRIDE;

	/**
	 * Returns the number of detect HMDs.
	 */
	virtual uint32_t GetHmdCount() OSVR_OVERRIDE;

	/**
	 * Returns a single HMD by its index.
	 *
	 * @param index the index of the HMD to return.
	 */
	virtual vr::IHmdDriver* GetHmd(uint32_t index) OSVR_OVERRIDE;

	/**
	 * Returns a single HMD by its name.
	 *
	 * @param hmd_id the C string name of the HMD.
	 */
	virtual vr::IHmdDriver* FindHmd(const char* hmd_id) OSVR_OVERRIDE;

private:
	std::vector<std::unique_ptr<OSVRHmd>> hmds_;
	std::unique_ptr<osvr::clientkit::ClientContext> context_;
	std::unique_ptr<ClientMainloopThread> client_;
};

CDriver_OSVR g_driverOSVR;

vr::HmdError CDriver_OSVR::Init(const char* pchUserConfigDir, const char* pchDriverInstallDir)
{
	context_ = std::make_unique<osvr::clientkit::ClientContext>("com.osvr.SteamVR");

	client_ = std::make_unique<ClientMainloopThread>(*context_);

	const std::string display_description = context_->getStringParameter("/display");
	osvr::clientkit::Interface head_tracker_interface = context_->getInterface("/me/head");
	hmds_.push_back(std::make_unique<OSVRHmd>(display_description, &head_tracker_interface));

	client_->start();

	return vr::HmdError_None;
}

void CDriver_OSVR::Cleanup()
{
	// do nothing
}

uint32_t CDriver_OSVR::GetHmdCount()
{
	return hmds_.size();
}

vr::IHmdDriver* CDriver_OSVR::GetHmd(uint32_t index)
{
	if (index >= hmds_.size())
		return NULL;

	return hmds_[index].get();
}

vr::IHmdDriver* CDriver_OSVR::FindHmd(const char* hmd_id)
{
	for (auto& hmd : hmds_) {
		if (0 == std::strcmp(hmd_id, hmd->GetId()))
			return hmd.get();
	}

	return NULL;
}

static const char* IHmdDriverProvider_Prefix = "IHmdDriverProvider_";

OSVR_DLL_EXPORT void* HmdDriverFactory(const char* pInterfaceName, int* pReturnCode)
{
	if (!StringHasPrefix(pInterfaceName, IHmdDriverProvider_Prefix)) {
		*pReturnCode = vr::HmdError_Init_InvalidInterface;
		return NULL;
	}

	if (0 != strcmp(vr::IHmdDriverProvider_Version, pInterfaceName)) {
		if (pReturnCode)
			*pReturnCode = vr::HmdError_Init_InterfaceNotFound;
		return NULL;
	}

	return &g_driverOSVR;
}

<commit_msg>Do an emplace_back instead of push_back.<commit_after>/** @file
    @brief OSVR driver provider

    @date 2015

    @author
    Sensics, Inc.
    <http://sensics.com>

*/

// Copyright 2015 Sensics, Inc.
//
// All rights reserved.
//
// (Final version intended to be licensed under
// the Apache License, Version 2.0)

// Internal Includes
#include "ihmddriverprovider.h"
#include "ihmddriver.h"
#include "steamvr.h"

#include "osvr_compiler_detection.h"
#include "osvr_hmd.h"

#include "stringhasprefix.h"
#include "osvr_dll_export.h"

// Library/third-party includes
#include <osvr/ClientKit/Context.h>
#include <osvr/ClientKit/Interface.h>

// Standard includes
#include <vector>
#include <cstring>

class CDriver_OSVR : public vr::IHmdDriverProvider {
public:
	/**
	 * Initializes the driver.
	 *
	 * This is called when the driver is first loaded.
	 *
	 * @param user_config_dir the absoluate path of the directory where the
	 *     driver should store any user configuration files.
	 * @param driver_install_dir the absolute path of the driver's root
	 *     directory.
	 *
	 * If Init() returns anything other than \c HmdError_None the driver will be
	 * unloaded.
	 *
	 * @returns HmdError_None on success.
	 */
	virtual vr::HmdError Init(const char* pchUserConfigDir, const char* pchDriverInstallDir) OSVR_OVERRIDE;

	/**
	 * Performs any cleanup prior to the driver being unloaded.
	 */
	virtual void Cleanup() OSVR_OVERRIDE;

	/**
	 * Returns the number of detect HMDs.
	 */
	virtual uint32_t GetHmdCount() OSVR_OVERRIDE;

	/**
	 * Returns a single HMD by its index.
	 *
	 * @param index the index of the HMD to return.
	 */
	virtual vr::IHmdDriver* GetHmd(uint32_t index) OSVR_OVERRIDE;

	/**
	 * Returns a single HMD by its name.
	 *
	 * @param hmd_id the C string name of the HMD.
	 */
	virtual vr::IHmdDriver* FindHmd(const char* hmd_id) OSVR_OVERRIDE;

private:
	std::vector<std::unique_ptr<OSVRHmd>> hmds_;
	std::unique_ptr<osvr::clientkit::ClientContext> context_;
	std::unique_ptr<ClientMainloopThread> client_;
};

CDriver_OSVR g_driverOSVR;

vr::HmdError CDriver_OSVR::Init(const char* pchUserConfigDir, const char* pchDriverInstallDir)
{
	context_ = std::make_unique<osvr::clientkit::ClientContext>("com.osvr.SteamVR");

	client_ = std::make_unique<ClientMainloopThread>(*context_);

	const std::string display_description = context_->getStringParameter("/display");
	osvr::clientkit::Interface head_tracker_interface = context_->getInterface("/me/head");
	hmds_.emplace_back(std::make_unique<OSVRHmd>(display_description, &head_tracker_interface));

	client_->start();

	return vr::HmdError_None;
}

void CDriver_OSVR::Cleanup()
{
	// do nothing
}

uint32_t CDriver_OSVR::GetHmdCount()
{
	return hmds_.size();
}

vr::IHmdDriver* CDriver_OSVR::GetHmd(uint32_t index)
{
	if (index >= hmds_.size())
		return NULL;

	return hmds_[index].get();
}

vr::IHmdDriver* CDriver_OSVR::FindHmd(const char* hmd_id)
{
	for (auto& hmd : hmds_) {
		if (0 == std::strcmp(hmd_id, hmd->GetId()))
			return hmd.get();
	}

	return NULL;
}

static const char* IHmdDriverProvider_Prefix = "IHmdDriverProvider_";

OSVR_DLL_EXPORT void* HmdDriverFactory(const char* pInterfaceName, int* pReturnCode)
{
	if (!StringHasPrefix(pInterfaceName, IHmdDriverProvider_Prefix)) {
		*pReturnCode = vr::HmdError_Init_InvalidInterface;
		return NULL;
	}

	if (0 != strcmp(vr::IHmdDriverProvider_Version, pInterfaceName)) {
		if (pReturnCode)
			*pReturnCode = vr::HmdError_Init_InterfaceNotFound;
		return NULL;
	}

	return &g_driverOSVR;
}

<|endoftext|>


<commit_before>#include "chimera/visitor.h"

#include <algorithm>
#include <boost/algorithm/string/join.hpp>
#include <iostream>
#include <llvm/Support/raw_ostream.h>
#include <string>

using namespace chimera;
using namespace clang;
using boost::algorithm::join;

// TODO: Support template functions.
// TODO: Detect missing copy constructors, possibly using:
//  hasUserDeclaredCopyConstructor()
//  hasCopyConstructorWithConstParam ()
// TODO: Mark abstract classes as noncopyable?

chimera::Visitor::Visitor(clang::CompilerInstance *ci,
                          std::unique_ptr<CompiledConfiguration> cc)
: context_(&(ci->getASTContext()))
, printing_policy_(ci->getLangOpts())
, config_(std::move(cc))
{
    // Do nothing.
}

bool chimera::Visitor::VisitDecl(Decl *decl)
{
    // Only visit declarations in namespaces we are configured to read.
    if (!IsEnclosed(decl))
        return true;

    if (isa<ClassTemplateDecl>(decl))
        GenerateClassTemplate(cast<ClassTemplateDecl>(decl));
    else if (isa<CXXRecordDecl>(decl))
    {
        // Every class template is represented by a CXXRecordDecl and a
        // ClassTemplateDecl. We handle code generation of template classes in
        // the above case, so we don't process them here.
        CXXRecordDecl *class_decl = cast<CXXRecordDecl>(decl);
        if (!class_decl->getDescribedClassTemplate())
            GenerateCXXRecord(class_decl);
    }
    else if (isa<EnumDecl>(decl))
        GenerateEnum(cast<EnumDecl>(decl));
    else if (isa<VarDecl>(decl))
        GenerateGlobalVar(cast<VarDecl>(decl));
    else if (isa<FunctionDecl>(decl))
        GenerateGlobalFunction(cast<FunctionDecl>(decl));

    return true;
}

bool chimera::Visitor::GenerateCXXRecord(CXXRecordDecl *const decl)
{
    // Only traverse CXX records that contain the actual class definition.
    if (!decl->hasDefinition() || decl->getDefinition() != decl)
        return false;

    // Open a stream object unique to this CXX record's mangled name.
    auto stream = config_->GetOutputFile(decl);
    if (!stream)
        return false;

    // Get configuration, and use any overrides if they exist.
    if (config_->DumpOverride(decl, *stream))
        return true;

    const YAML::Node &node = config_->GetDeclaration(decl);
    if (node.IsNull())
        return false; // Explicitly suppressed.

    *stream << "::boost::python::class_<"
            << decl->getTypeForDecl()->getCanonicalTypeInternal().getAsString(printing_policy_);

    const YAML::Node &noncopyable_node = node["noncopyable"];
    if (noncopyable_node && noncopyable_node.as<bool>(false))
        *stream << ", ::boost::noncopyable";

    if (const YAML::Node &held_type_node = node["held_type"])
        *stream << ", " << held_type_node.as<std::string>();

    std::vector<std::string> base_names
        = node["bases"].as<std::vector<std::string> >(GetBaseClassNames(decl));
    if (!base_names.empty())
    {
        *stream << ", ::boost::python::bases<"
                  << join(base_names, ", ") << " >";
    }

    *stream << " >(\"" << node["name"].as<std::string>(decl->getNameAsString())
            << "\", boost::python::no_init)\n";

    // Methods
    std::set<std::string> overloaded_method_names;

    for (CXXMethodDecl *const method_decl : decl->methods())
    {
        if (method_decl->getAccess() != AS_public)
            continue; // skip protected and private members

        if (isa<CXXConversionDecl>(method_decl))
            ; // do nothing
        else if (isa<CXXDestructorDecl>(method_decl))
            ; // do nothing
        else if (isa<CXXConstructorDecl>(method_decl))
        {
            GenerateCXXConstructor(
                *stream, decl, cast<CXXConstructorDecl>(method_decl));
        }
        else if (method_decl->isOverloadedOperator())
            ; // TODO: Wrap overloaded operators.
        else if (method_decl->isStatic())
        {
            // TODO: Missing the dot.
            if (GenerateFunction(*stream, decl, method_decl))
                overloaded_method_names.insert(method_decl->getNameAsString());
        }
        else
        {
            // TODO: Missing the dot.
            GenerateFunction(*stream, decl, method_decl);
        }
    }

    // Static methods MUST be declared after overloads are defined.
    for (const std::string &method_name : overloaded_method_names)
        *stream << ".staticmethod(\"" << method_name << "\")\n";

    // Fields
    for (FieldDecl *const field_decl : decl->fields())
    {
        if (field_decl->getAccess() != AS_public)
            continue; // skip protected and private fields

        GenerateField(*stream, decl, field_decl);
    }

    for (Decl *const child_decl : decl->decls())
    {
        if (isa<VarDecl>(child_decl))
            GenerateStaticField(*stream, decl, cast<VarDecl>(child_decl));
    }

    *stream << ";\n";

    return true;
}

bool chimera::Visitor::GenerateCXXConstructor(
    chimera::Stream &stream,
    CXXRecordDecl *class_decl,
    CXXConstructorDecl *decl)
{
    decl = decl->getCanonicalDecl();

    if (decl->isDeleted())
        return false;

    std::vector<std::string> argument_types;

    for (ParmVarDecl *param_decl : decl->params())
        argument_types.push_back(param_decl->getType().getAsString(printing_policy_));

    stream << ".def(::boost::python::init<"
           << join(argument_types, ", ")
           << ">())\n";
    return true;
}

bool chimera::Visitor::GenerateFunction(
    chimera::Stream &stream,
    CXXRecordDecl *class_decl, FunctionDecl *decl)
{
    decl = decl->getCanonicalDecl();

    if (decl->isDeleted())
        return false;

    // Get configuration, and use any overrides if they exist.
    if (config_->DumpOverride(decl, stream))
        return true;

    const YAML::Node &node = config_->GetDeclaration(decl);
    if (node.IsNull())
        return false; // Explicitly suppressed.

    // Extract the pointer type of this function declaration.
    QualType pointer_type;
    if (class_decl && !cast<CXXMethodDecl>(decl)->isStatic())
    {
        pointer_type = context_->getMemberPointerType(
            decl->getType(), class_decl->getTypeForDecl());
    }
    else
    {
        pointer_type = context_->getPointerType(decl->getType());
    }
    pointer_type = pointer_type.getCanonicalType();

    // Extract the return type of this function declaration.
    const QualType return_qual_type = decl->getReturnType();
    const Type *return_type = return_qual_type.getTypePtr();

    // First, check if a return_value_policy was specified for this function.
    std::string return_value_policy
        = node["return_value_policy"].as<std::string>("");

    // Next, check if a return_value_policy is defined on the return type.
    if (return_value_policy.empty())
    {
        const YAML::Node &type_node = config_->GetType(return_qual_type);
        return_value_policy
            = type_node["return_value_policy"].as<std::string>("");
    }

    // Finally, try the default return_value_policy. This is only acceptable if
    // the output is copy constructable.
    if (return_value_policy.empty())
    {
        if (return_type->isReferenceType())
        {
            std::cerr
                << "Warning: Skipped method '"
                << pointer_type.getAsString(printing_policy_)
                << "' because it returns a reference and no"
                   " 'return_value_policy' was specified.\n";
            return false;
        }
        else if (return_type->isPointerType())
        {
            std::cerr
                << "Warning: Skipped method '"
                << pointer_type.getAsString(printing_policy_)
                << "' because it returns a pointer and no"
                   " 'return_value_policy' was specified.\n";
            return false;
        }
        // TODO: Check if return_type is non-copyable.
    }

    // If we are inside a class declaration, this is being called within a
    // builder pattern and will start with '.' since it is a member function.
    if (class_decl)
        stream << ".";
    else
        stream << "boost::python::";

    // Create the actual function declaration here using its name and its
    // full pointer reference.
    stream << "def(\"" << decl->getNameAsString() << "\""
           << ", static_cast<" << pointer_type.getAsString(printing_policy_) << ">(&"
           << decl->getQualifiedNameAsString() << ")";

    // If a return value policy was specified, insert it after the function.
    if (!return_value_policy.empty())
    {
        stream << ", ::boost::python::return_value_policy<"
               << return_value_policy << " >()";
    }

    // Construct a list of the arguments that are provided to this function,
    // and define named arguments for them based on their c++ names.
    const auto params = GetParameterNames(decl);
    if (!params.empty())
    {
        // TODO: Suppress any default parameters that occur after the first
        // non-default to default transition. This can only occur if evaluating
        // the default value of one or more parameters failed.

        // TODO: Assign names to unnamed arguments.

        std::vector<std::string> python_args;
        for (const auto &param : params)
        {
            std::stringstream python_arg;
            python_arg << "::boost::python::arg(\"" << param.first << "\")";

            if (!param.second.empty())
                python_arg << " = " << param.second;

            python_args.push_back(python_arg.str());
        }

        stream << ", (" << join(python_args, ", ") << ")";
    }

    stream << ")\n";

    if (!class_decl)
        stream << ";";

    return true;
}

bool chimera::Visitor::GenerateField(
    chimera::Stream &stream,
    clang::CXXRecordDecl *class_decl,
    clang::FieldDecl *decl)
{
    if (decl->getType().isConstQualified())
        stream << ".def_readonly";
    else
        stream << ".def_readwrite";

    // TODO: Check if a copy constructor is defined for this type.

    stream << "(\"" << decl->getNameAsString() << "\","
           << " &" << decl->getQualifiedNameAsString() << ")\n";
    return true;
}

bool chimera::Visitor::GenerateStaticField(
    chimera::Stream &stream,
    clang::CXXRecordDecl *class_decl,
    clang::VarDecl *decl)
{
    if (decl->getAccess() != AS_public)
        return false;
    else if (!decl->isStaticDataMember())
        return false;

    stream << ".add_static_property(\"" << decl->getNameAsString() << "\", "
           << "[]() { return " << decl->getQualifiedNameAsString() << "; }";

    if (!decl->getType().isConstQualified())
    {
        stream << "[](" << decl->getType().getAsString(printing_policy_) << " value) { "
               << decl->getQualifiedNameAsString() << " = value; }";
    }

    stream << ")\n";

    return true;
}

bool chimera::Visitor::GenerateClassTemplate(clang::ClassTemplateDecl *decl)
{
    if (decl != decl->getCanonicalDecl())
        return false;

    for (ClassTemplateSpecializationDecl *spec_decl : decl->specializations())
    {
        if (spec_decl->getSpecializationKind() == TSK_Undeclared)
            continue;

        CXXRecordDecl *decl = spec_decl->getTypeForDecl()->getAsCXXRecordDecl();
        GenerateCXXRecord(decl);
    }

    return true;
}

bool chimera::Visitor::GenerateEnum(clang::EnumDecl *decl)
{
    auto stream = config_->GetOutputFile(decl);
    if (!stream)
        return false;

    *stream << "::boost::python::enum_<"
            << decl->getQualifiedNameAsString()
            << ">(\"" << decl->getNameAsString() << "\")\n";

    for (EnumConstantDecl *constant_decl : decl->enumerators())
    {
        *stream << ".value(\"" << constant_decl->getNameAsString() << "\", "
                << constant_decl->getQualifiedNameAsString() << ")\n";
    }

    *stream << ";\n";

    return true;
}

bool chimera::Visitor::GenerateGlobalVar(clang::VarDecl *decl)
{
    if (!decl->isFileVarDecl())
        return false;
    else if (!decl->isThisDeclarationADefinition())
        return false;

    auto stream = config_->GetOutputFile(decl);
    if (!stream)
        return false;

    *stream << "::boost::python::scope().attr(\"" << decl->getNameAsString()
            << "\") = " << decl->getQualifiedNameAsString() << ";\n";
    return true;
}

bool chimera::Visitor::GenerateGlobalFunction(clang::FunctionDecl *decl)
{
    if (isa<clang::CXXMethodDecl>(decl))
        return false;
    else if (decl->isOverloadedOperator())
        return false; // TODO: Wrap overloaded operators.

    auto stream = config_->GetOutputFile(decl);
    if (!stream)
        return false;

    return GenerateFunction(*stream, nullptr, decl);
}

std::vector<std::string> chimera::Visitor::GetBaseClassNames(
    CXXRecordDecl *decl) const
{
    std::vector<std::string> base_names;

    for (CXXBaseSpecifier &base_decl : decl->bases())
    {
        if (base_decl.getAccessSpecifier() != AS_public)
            continue;

        // TODO: Filter out transitive base classes.

        CXXRecordDecl *const base_record_decl
          = base_decl.getType()->getAsCXXRecordDecl();
        const QualType base_record_type
          = base_record_decl->getTypeForDecl()->getCanonicalTypeInternal();

        base_names.push_back(base_record_type.getAsString(printing_policy_));
    }

    return base_names;
}

std::vector<std::pair<std::string, std::string>>
    chimera::Visitor::GetParameterNames(clang::FunctionDecl *decl) const
{
    std::vector<std::pair<std::string, std::string>> params;

    for (ParmVarDecl *param_decl : decl->params())
    {
        const std::string param_name = param_decl->getNameAsString();
        const Type *param_type = param_decl->getType().getTypePtr();
        std::string param_value;

        if (param_decl->hasDefaultArg()
            && !param_decl->hasUninstantiatedDefaultArg()
            && !param_decl->hasUnparsedDefaultArg())
        {
            Expr *default_expr = param_decl->getDefaultArg();
            assert(default_expr);

            Expr::EvalResult result;
            bool success;

            if (param_type->isReferenceType())
                success = default_expr->EvaluateAsLValue(result, *context_);
            else
                success = default_expr->EvaluateAsRValue(result, *context_);

            if (success)
            {
                param_value = result.Val.getAsString(
                    *context_, param_decl->getType());
            }
            else if (default_expr->hasNonTrivialCall(*context_))
            {
                // TODO: How do we print the decl with argument + return types?
                std::cerr
                  << "Warning: Unable to evaluate non-trivial call in default"
                     " value for parameter"
                  << " '" << param_name << "' of method"
                  << " '" << decl->getQualifiedNameAsString() << "'.\n";
            }
            else
            {
                // TODO: How do we print the decl with argument + return types?
                std::cerr
                  << "Warning: Failed to evaluate default value for parameter"
                  << " '" << param_name << "' of method"
                  << " '" << decl->getQualifiedNameAsString() << "'.\n";
            }
        }

        params.push_back(std::make_pair(param_name, param_value));
    }

    return params;
}

bool chimera::Visitor::IsEnclosed(Decl *decl) const
{
    // Filter over the namespaces and only traverse ones that are enclosed
    // by one of the configuration namespaces.
    for (const auto &it : config_->GetNamespaces())
    {
        if (decl->getDeclContext() && it->Encloses(decl->getDeclContext()))
        {
            return true;
        }
    }
    return false;
}
<commit_msg>Fixed non-canonical type strings in constructor.<commit_after>#include "chimera/visitor.h"

#include <algorithm>
#include <boost/algorithm/string/join.hpp>
#include <iostream>
#include <llvm/Support/raw_ostream.h>
#include <string>

using namespace chimera;
using namespace clang;
using boost::algorithm::join;

// TODO: Support template functions.
// TODO: Detect missing copy constructors, possibly using:
//  hasUserDeclaredCopyConstructor()
//  hasCopyConstructorWithConstParam ()
// TODO: Mark abstract classes as noncopyable?

chimera::Visitor::Visitor(clang::CompilerInstance *ci,
                          std::unique_ptr<CompiledConfiguration> cc)
: context_(&(ci->getASTContext()))
, printing_policy_(ci->getLangOpts())
, config_(std::move(cc))
{
    // Do nothing.
}

bool chimera::Visitor::VisitDecl(Decl *decl)
{
    // Only visit declarations in namespaces we are configured to read.
    if (!IsEnclosed(decl))
        return true;

    if (isa<ClassTemplateDecl>(decl))
        GenerateClassTemplate(cast<ClassTemplateDecl>(decl));
    else if (isa<CXXRecordDecl>(decl))
    {
        // Every class template is represented by a CXXRecordDecl and a
        // ClassTemplateDecl. We handle code generation of template classes in
        // the above case, so we don't process them here.
        CXXRecordDecl *class_decl = cast<CXXRecordDecl>(decl);
        if (!class_decl->getDescribedClassTemplate())
            GenerateCXXRecord(class_decl);
    }
    else if (isa<EnumDecl>(decl))
        GenerateEnum(cast<EnumDecl>(decl));
    else if (isa<VarDecl>(decl))
        GenerateGlobalVar(cast<VarDecl>(decl));
    else if (isa<FunctionDecl>(decl))
        GenerateGlobalFunction(cast<FunctionDecl>(decl));

    return true;
}

bool chimera::Visitor::GenerateCXXRecord(CXXRecordDecl *const decl)
{
    // Only traverse CXX records that contain the actual class definition.
    if (!decl->hasDefinition() || decl->getDefinition() != decl)
        return false;

    // Open a stream object unique to this CXX record's mangled name.
    auto stream = config_->GetOutputFile(decl);
    if (!stream)
        return false;

    // Get configuration, and use any overrides if they exist.
    if (config_->DumpOverride(decl, *stream))
        return true;

    const YAML::Node &node = config_->GetDeclaration(decl);
    if (node.IsNull())
        return false; // Explicitly suppressed.

    *stream << "::boost::python::class_<"
            << decl->getTypeForDecl()->getCanonicalTypeInternal().getAsString(printing_policy_);

    const YAML::Node &noncopyable_node = node["noncopyable"];
    if (noncopyable_node && noncopyable_node.as<bool>(false))
        *stream << ", ::boost::noncopyable";

    if (const YAML::Node &held_type_node = node["held_type"])
        *stream << ", " << held_type_node.as<std::string>();

    std::vector<std::string> base_names
        = node["bases"].as<std::vector<std::string> >(GetBaseClassNames(decl));
    if (!base_names.empty())
    {
        *stream << ", ::boost::python::bases<"
                  << join(base_names, ", ") << " >";
    }

    *stream << " >(\"" << node["name"].as<std::string>(decl->getNameAsString())
            << "\", boost::python::no_init)\n";

    // Methods
    std::set<std::string> overloaded_method_names;

    for (CXXMethodDecl *const method_decl : decl->methods())
    {
        if (method_decl->getAccess() != AS_public)
            continue; // skip protected and private members

        if (isa<CXXConversionDecl>(method_decl))
            ; // do nothing
        else if (isa<CXXDestructorDecl>(method_decl))
            ; // do nothing
        else if (isa<CXXConstructorDecl>(method_decl))
        {
            GenerateCXXConstructor(
                *stream, decl, cast<CXXConstructorDecl>(method_decl));
        }
        else if (method_decl->isOverloadedOperator())
            ; // TODO: Wrap overloaded operators.
        else if (method_decl->isStatic())
        {
            // TODO: Missing the dot.
            if (GenerateFunction(*stream, decl, method_decl))
                overloaded_method_names.insert(method_decl->getNameAsString());
        }
        else
        {
            // TODO: Missing the dot.
            GenerateFunction(*stream, decl, method_decl);
        }
    }

    // Static methods MUST be declared after overloads are defined.
    for (const std::string &method_name : overloaded_method_names)
        *stream << ".staticmethod(\"" << method_name << "\")\n";

    // Fields
    for (FieldDecl *const field_decl : decl->fields())
    {
        if (field_decl->getAccess() != AS_public)
            continue; // skip protected and private fields

        GenerateField(*stream, decl, field_decl);
    }

    for (Decl *const child_decl : decl->decls())
    {
        if (isa<VarDecl>(child_decl))
            GenerateStaticField(*stream, decl, cast<VarDecl>(child_decl));
    }

    *stream << ";\n";

    return true;
}

bool chimera::Visitor::GenerateCXXConstructor(
    chimera::Stream &stream,
    CXXRecordDecl *class_decl,
    CXXConstructorDecl *decl)
{
    decl = decl->getCanonicalDecl();

    if (decl->isDeleted())
        return false;

    std::vector<std::string> argument_types;

    for (ParmVarDecl *param_decl : decl->params())
        argument_types.push_back(param_decl->getType()
                                            .getCanonicalType()
                                            .getAsString(printing_policy_));

    stream << ".def(::boost::python::init<"
           << join(argument_types, ", ")
           << ">())\n";
    return true;
}

bool chimera::Visitor::GenerateFunction(
    chimera::Stream &stream,
    CXXRecordDecl *class_decl, FunctionDecl *decl)
{
    decl = decl->getCanonicalDecl();

    if (decl->isDeleted())
        return false;

    // Get configuration, and use any overrides if they exist.
    if (config_->DumpOverride(decl, stream))
        return true;

    const YAML::Node &node = config_->GetDeclaration(decl);
    if (node.IsNull())
        return false; // Explicitly suppressed.

    // Extract the pointer type of this function declaration.
    QualType pointer_type;
    if (class_decl && !cast<CXXMethodDecl>(decl)->isStatic())
    {
        pointer_type = context_->getMemberPointerType(
            decl->getType(), class_decl->getTypeForDecl());
    }
    else
    {
        pointer_type = context_->getPointerType(decl->getType());
    }
    pointer_type = pointer_type.getCanonicalType();

    // Extract the return type of this function declaration.
    const QualType return_qual_type = decl->getReturnType();
    const Type *return_type = return_qual_type.getTypePtr();

    // First, check if a return_value_policy was specified for this function.
    std::string return_value_policy
        = node["return_value_policy"].as<std::string>("");

    // Next, check if a return_value_policy is defined on the return type.
    if (return_value_policy.empty())
    {
        const YAML::Node &type_node = config_->GetType(return_qual_type);
        return_value_policy
            = type_node["return_value_policy"].as<std::string>("");
    }

    // Finally, try the default return_value_policy. This is only acceptable if
    // the output is copy constructable.
    if (return_value_policy.empty())
    {
        if (return_type->isReferenceType())
        {
            std::cerr
                << "Warning: Skipped method '"
                << pointer_type.getAsString(printing_policy_)
                << "' because it returns a reference and no"
                   " 'return_value_policy' was specified.\n";
            return false;
        }
        else if (return_type->isPointerType())
        {
            std::cerr
                << "Warning: Skipped method '"
                << pointer_type.getAsString(printing_policy_)
                << "' because it returns a pointer and no"
                   " 'return_value_policy' was specified.\n";
            return false;
        }
        // TODO: Check if return_type is non-copyable.
    }

    // If we are inside a class declaration, this is being called within a
    // builder pattern and will start with '.' since it is a member function.
    if (class_decl)
        stream << ".";
    else
        stream << "boost::python::";

    // Create the actual function declaration here using its name and its
    // full pointer reference.
    stream << "def(\"" << decl->getNameAsString() << "\""
           << ", static_cast<" << pointer_type.getAsString(printing_policy_) << ">(&"
           << decl->getQualifiedNameAsString() << ")";

    // If a return value policy was specified, insert it after the function.
    if (!return_value_policy.empty())
    {
        stream << ", ::boost::python::return_value_policy<"
               << return_value_policy << " >()";
    }

    // Construct a list of the arguments that are provided to this function,
    // and define named arguments for them based on their c++ names.
    const auto params = GetParameterNames(decl);
    if (!params.empty())
    {
        // TODO: Suppress any default parameters that occur after the first
        // non-default to default transition. This can only occur if evaluating
        // the default value of one or more parameters failed.

        // TODO: Assign names to unnamed arguments.

        std::vector<std::string> python_args;
        for (const auto &param : params)
        {
            std::stringstream python_arg;
            python_arg << "::boost::python::arg(\"" << param.first << "\")";

            if (!param.second.empty())
                python_arg << " = " << param.second;

            python_args.push_back(python_arg.str());
        }

        stream << ", (" << join(python_args, ", ") << ")";
    }

    stream << ")\n";

    if (!class_decl)
        stream << ";";

    return true;
}

bool chimera::Visitor::GenerateField(
    chimera::Stream &stream,
    clang::CXXRecordDecl *class_decl,
    clang::FieldDecl *decl)
{
    if (decl->getType().isConstQualified())
        stream << ".def_readonly";
    else
        stream << ".def_readwrite";

    // TODO: Check if a copy constructor is defined for this type.

    stream << "(\"" << decl->getNameAsString() << "\","
           << " &" << decl->getQualifiedNameAsString() << ")\n";
    return true;
}

bool chimera::Visitor::GenerateStaticField(
    chimera::Stream &stream,
    clang::CXXRecordDecl *class_decl,
    clang::VarDecl *decl)
{
    if (decl->getAccess() != AS_public)
        return false;
    else if (!decl->isStaticDataMember())
        return false;

    stream << ".add_static_property(\"" << decl->getNameAsString() << "\", "
           << "[]() { return " << decl->getQualifiedNameAsString() << "; }";

    if (!decl->getType().isConstQualified())
    {
        stream << "[](" << decl->getType().getAsString(printing_policy_) << " value) { "
               << decl->getQualifiedNameAsString() << " = value; }";
    }

    stream << ")\n";

    return true;
}

bool chimera::Visitor::GenerateClassTemplate(clang::ClassTemplateDecl *decl)
{
    if (decl != decl->getCanonicalDecl())
        return false;

    for (ClassTemplateSpecializationDecl *spec_decl : decl->specializations())
    {
        if (spec_decl->getSpecializationKind() == TSK_Undeclared)
            continue;

        CXXRecordDecl *decl = spec_decl->getTypeForDecl()->getAsCXXRecordDecl();
        GenerateCXXRecord(decl);
    }

    return true;
}

bool chimera::Visitor::GenerateEnum(clang::EnumDecl *decl)
{
    auto stream = config_->GetOutputFile(decl);
    if (!stream)
        return false;

    *stream << "::boost::python::enum_<"
            << decl->getQualifiedNameAsString()
            << ">(\"" << decl->getNameAsString() << "\")\n";

    for (EnumConstantDecl *constant_decl : decl->enumerators())
    {
        *stream << ".value(\"" << constant_decl->getNameAsString() << "\", "
                << constant_decl->getQualifiedNameAsString() << ")\n";
    }

    *stream << ";\n";

    return true;
}

bool chimera::Visitor::GenerateGlobalVar(clang::VarDecl *decl)
{
    if (!decl->isFileVarDecl())
        return false;
    else if (!decl->isThisDeclarationADefinition())
        return false;

    auto stream = config_->GetOutputFile(decl);
    if (!stream)
        return false;

    *stream << "::boost::python::scope().attr(\"" << decl->getNameAsString()
            << "\") = " << decl->getQualifiedNameAsString() << ";\n";
    return true;
}

bool chimera::Visitor::GenerateGlobalFunction(clang::FunctionDecl *decl)
{
    if (isa<clang::CXXMethodDecl>(decl))
        return false;
    else if (decl->isOverloadedOperator())
        return false; // TODO: Wrap overloaded operators.

    auto stream = config_->GetOutputFile(decl);
    if (!stream)
        return false;

    return GenerateFunction(*stream, nullptr, decl);
}

std::vector<std::string> chimera::Visitor::GetBaseClassNames(
    CXXRecordDecl *decl) const
{
    std::vector<std::string> base_names;

    for (CXXBaseSpecifier &base_decl : decl->bases())
    {
        if (base_decl.getAccessSpecifier() != AS_public)
            continue;

        // TODO: Filter out transitive base classes.

        CXXRecordDecl *const base_record_decl
          = base_decl.getType()->getAsCXXRecordDecl();
        const QualType base_record_type
          = base_record_decl->getTypeForDecl()->getCanonicalTypeInternal();

        base_names.push_back(base_record_type.getAsString(printing_policy_));
    }

    return base_names;
}

std::vector<std::pair<std::string, std::string>>
    chimera::Visitor::GetParameterNames(clang::FunctionDecl *decl) const
{
    std::vector<std::pair<std::string, std::string>> params;

    for (ParmVarDecl *param_decl : decl->params())
    {
        const std::string param_name = param_decl->getNameAsString();
        const Type *param_type = param_decl->getType().getTypePtr();
        std::string param_value;

        if (param_decl->hasDefaultArg()
            && !param_decl->hasUninstantiatedDefaultArg()
            && !param_decl->hasUnparsedDefaultArg())
        {
            Expr *default_expr = param_decl->getDefaultArg();
            assert(default_expr);

            Expr::EvalResult result;
            bool success;

            if (param_type->isReferenceType())
                success = default_expr->EvaluateAsLValue(result, *context_);
            else
                success = default_expr->EvaluateAsRValue(result, *context_);

            if (success)
            {
                param_value = result.Val.getAsString(
                    *context_, param_decl->getType());
            }
            else if (default_expr->hasNonTrivialCall(*context_))
            {
                // TODO: How do we print the decl with argument + return types?
                std::cerr
                  << "Warning: Unable to evaluate non-trivial call in default"
                     " value for parameter"
                  << " '" << param_name << "' of method"
                  << " '" << decl->getQualifiedNameAsString() << "'.\n";
            }
            else
            {
                // TODO: How do we print the decl with argument + return types?
                std::cerr
                  << "Warning: Failed to evaluate default value for parameter"
                  << " '" << param_name << "' of method"
                  << " '" << decl->getQualifiedNameAsString() << "'.\n";
            }
        }

        params.push_back(std::make_pair(param_name, param_value));
    }

    return params;
}

bool chimera::Visitor::IsEnclosed(Decl *decl) const
{
    // Filter over the namespaces and only traverse ones that are enclosed
    // by one of the configuration namespaces.
    for (const auto &it : config_->GetNamespaces())
    {
        if (decl->getDeclContext() && it->Encloses(decl->getDeclContext()))
        {
            return true;
        }
    }
    return false;
}
<|endoftext|>


<commit_before>// Copyright 2010-2012 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <math.h>
#include <string.h>
#include <algorithm>
#include "base/hash.h"
#include <string>
#include <utility>
#include <vector>

#include "base/commandlineflags.h"
#include "base/hash.h"
#include "base/int-type-indexed-vector.h"
#include "base/int-type.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/map-util.h"
#include "base/scoped_ptr.h"
#include "base/stringprintf.h"
#include "constraint_solver/constraint_solver.h"
#include "constraint_solver/constraint_solveri.h"
#include "util/bitset.h"
#include "util/const_int_array.h"

namespace operations_research {
namespace {
DEFINE_INT_TYPE(AtomIndex, int);

const static AtomIndex kFailAtom = AtomIndex(0);

template <class T> class UnorderedRevArray {
 public:
  UnorderedRevArray(int initial_capacity)
  : elements_(), num_elements_(0) {
    elements_.reserve(initial_capacity);
  }

  UnorderedRevArray(const std::vector<T>& elements)
      : elements_(elements), num_elements_(elements.size()) {}

  ~UnorderedRevArray() {}

  int Size() const { return num_elements_.Value(); }

  T Element(int i) const {
    DCHECK_GE(i, 0);
    DCHECK_LT(i, num_elements_.Value());
    return elements_[i];
  }

  void Insert(Solver* const solver, T elt) {
    elements_.push_back(elt);
    num_elements_.Incr(solver);
  }

  void Remove(Solver* const solver, int position_of_element_to_remove) {
    num_elements_.Decr(solver);
    SwapTo(position_of_element_to_remove, num_elements_.Value());
  }

  void Clear(Solver* const solver) {
    num_elements_.SetValue(solver, 0);
  }

 private:
  void SwapTo(int current_position, int next_position) {
    if (current_position != next_position) {
      const T next_value = elements_[next_position];
      elements_[next_position] = elements_[current_position];
      elements_[current_position] = next_value;
    }
  }

  std::vector<T> elements_; // set of elements.
  NumericalRev<int> num_elements_; // number of elements in the set.
};

class Store;
class SumLessConstant;
class SumTriggerAction;

class Atom {
 public:
  Atom(AtomIndex index) : atom_index_(index) {}
  ~Atom() {}

  void Listen(SumLessConstant* const ct) {
    sum_less_constant_constraints_.push_back(ct);
  }

  void Listen(SumTriggerAction* const ct) {
    sum_trigger_actions_constraints_.insert(ct);
  }

  void StopListening(SumTriggerAction* const ct) {
    sum_trigger_actions_constraints_.erase(ct);
  }

  void AddFlipAction(AtomIndex action) {
    actions_.push_back(action);
  }

  void Flip(Store* const store);

  bool IsFlipped() const {
    return flipped_.Switched();
  }

 private:
  const AtomIndex atom_index_;
  std::vector<SumLessConstant*> sum_less_constant_constraints_;
  hash_set<SumTriggerAction*> sum_trigger_actions_constraints_;
  std::vector<AtomIndex> actions_;
  RevSwitch flipped_;
};

class Store : public Constraint {
 public:
  Store(Solver* const solver) : Constraint(solver) {}

  ~Store() {}

  AtomIndex TrueIndex(IntVar* const var) {
    int raw_index = indices_.Add(var);
    if (raw_index >= true_atoms_.size()) {
      true_atoms_.resize(raw_index);
      false_atoms_.resize(raw_index);
    }
    return 1 + AtomIndex(raw_index);
  }

  AtomIndex FalseIndex(IntVar* const var) {
    return -TrueIndex(var);
  }

  void VariableBound(int index) {
    if (indices_.Element(index)->Min() == 0) {
      Flip(AtomIndex(-1 - index));
    } else {
      Flip(AtomIndex(1 + index));
    }
  }

  void Listen(AtomIndex atom, SumLessConstant* const ct) {
    FindAtom(atom)->Listen(ct);
  }

  void Listen(AtomIndex atom, SumTriggerAction* const ct) {
    FindAtom(atom)->Listen(ct);
  }

  void StopListening(AtomIndex atom, SumTriggerAction* const ct) {
    FindAtom(atom)->StopListening(ct);
  }

  void Flip(AtomIndex atom) {
    if (atom == kFailAtom || IsFlipped(-atom)) {
      solver()->Fail();
    } else {
      FindAtom(atom)->Flip(this);
    }
  }

  bool IsFlipped(AtomIndex atom) {
    if (atom == kFailAtom) {
      return false;
    } else {
      return FindAtom(atom)->IsFlipped();
    }
  }

  virtual void Post() {
    for (int i = 0; i < indices_.size(); ++i) {
      Demon* const d = MakeConstraintDemon1(solver(),
                                            this,
                                            &Store::VariableBound,
                                            "VariableBound",
                                            i);
      indices_.Element(i)->WhenDomain(d);
    }
  }

  virtual void InitialPropagate() {
    for (int i = 0; i < indices_.size(); ++i) {
      if (indices_.Element(i)->Bound()) {
        VariableBound(i);
      }
    }
  }

 private:
  Atom* FindAtom(AtomIndex atom) const {
    CHECK_NE(atom, kFailAtom);
    if (atom > kFailAtom) {
      return true_atoms_[atom.value() - 1];
    } else {
      return false_atoms_[-1 - atom.value()];
    }
  }

  VectorMap<IntVar*> indices_;
  std::vector<Atom*> true_atoms_;
  std::vector<Atom*> false_atoms_;
};

class SumLessConstant {
 public:
  SumLessConstant(std::vector<AtomIndex>& vars, int constant)
      : vars_(vars),
        constant_(constant),
        sum_(0) {}

  ~SumLessConstant() {}

  void Post(Store* const store) {
    for (int i = 0; i < vars_.size(); ++i) {
      store->Listen(vars_[i], this);
    }
  }

  void Flip(Store* const store, AtomIndex index) {
    sum_.Incr(store->solver());
    if (sum_.Value() > constant_) {
      store->solver()->Fail();
    } else if (sum_.Value() == constant_) {
      UnflipAllPending(store);
    }
  }

  void UnflipAllPending(Store* const store) {
    int count = 0;
    for (int i = 0; i < vars_.size(); ++i) {
      if (!store->IsFlipped(vars_[i])) {
        count++;
        store->Flip(-vars_[i]);
      }
    }
    // Check count.
  }

 private:
  std::vector<AtomIndex> vars_;
  const int constant_;
  NumericalRev<int> sum_;
};

class SumTriggerAction {
 public:
  SumTriggerAction(std::vector<AtomIndex>& vars,
                   int constant,
                   std::vector<AtomIndex>& actions)
      : vars_(vars),
        constant_(constant),
        actions_(actions),
        sum_(0) {}

  ~SumTriggerAction() {}

  void Post(Store* const store) {
    for (int i = 0; i < vars_.size(); ++i) {
      store->Listen(vars_[i], this);
    }
  }

  void Flip(Store* const store, AtomIndex index) {
    sum_.Incr(store->solver());
    if (sum_.Value() >= constant_) {
      StopListening(store);
      FlipAllAction(store);
    }
  }

  void StopListening(Store* const store) {
    for (int i = 0; i < vars_.size(); ++i) {
      store->StopListening(vars_[i], this);
    }
  }

  void FlipAllAction(Store* const store) {
    for (int i = 0; i < actions_.size(); ++i) {
      store->Flip(actions_[i]);
    }
  }

 private:
  std::vector<AtomIndex> vars_;
  const int constant_;
  std::vector<AtomIndex> actions_;
  NumericalRev<int> sum_;
};

void Atom::Flip(Store* const store) {
  CHECK(!flipped_.Switched());
  flipped_.Switch(store->solver());
  for (int i = 0; i < actions_.size(); ++i) {
    store->Flip(actions_[i]);
  }
  for (int i = 0; i < sum_less_constant_constraints_.size(); ++i) {
    sum_less_constant_constraints_[i]->Flip(store, atom_index_);
  }
  for (ConstIter<hash_set<SumTriggerAction*> > iter(
           sum_trigger_actions_constraints_); !iter.at_end(); ++iter) {
    (*iter)->Flip(store, atom_index_);
  }
}
}  // namespace

}  // namespace operations_research
<commit_msg>more work on boolean propagator<commit_after>// Copyright 2010-2012 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <math.h>
#include <string.h>
#include <algorithm>
#include "base/hash.h"
#include <string>
#include <utility>
#include <vector>

#include "base/commandlineflags.h"
#include "base/hash.h"
#include "base/int-type-indexed-vector.h"
#include "base/int-type.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/map-util.h"
#include "base/scoped_ptr.h"
#include "base/stringprintf.h"
#include "constraint_solver/constraint_solver.h"
#include "constraint_solver/constraint_solveri.h"
#include "util/bitset.h"
#include "util/const_int_array.h"

namespace operations_research {
namespace {
DEFINE_INT_TYPE(AtomIndex, int);

const static AtomIndex kFailAtom = AtomIndex(0);

template <class T> class UnorderedRevArray {
 public:
  UnorderedRevArray() : elements_(), num_elements_(0) {}

  UnorderedRevArray(const std::vector<T>& elements)
      : elements_(elements), num_elements_(elements.size()) {}

  ~UnorderedRevArray() {}

  int Size() const { return num_elements_.Value(); }

  const T& operator[](int index) const {
    return Element(index);
  }

  const T& Element(int i) const {
    DCHECK_GE(i, 0);
    DCHECK_LT(i, num_elements_.Value());
    return elements_[i];
  }

  void Insert(Solver* const solver, T elt) {
    elements_.push_back(elt);
    num_elements_.Incr(solver);
  }

  void Remove(Solver* const solver, int position_of_element_to_remove) {
    num_elements_.Decr(solver);
    SwapTo(position_of_element_to_remove, num_elements_.Value());
  }

  void RemoveElement(Solver* const solver, const T& element) {
    for (int i = 0; i < num_elements_.Value(); ++i) {
      if (elements_[i] == element) {
        Remove(solver, i);
        return;
      }
    }
  }

  void Clear(Solver* const solver) {
    num_elements_.SetValue(solver, 0);
  }

 private:
  void SwapTo(int current_position, int next_position) {
    if (current_position != next_position) {
      const T next_value = elements_[next_position];
      elements_[next_position] = elements_[current_position];
      elements_[current_position] = next_value;
    }
  }

  std::vector<T> elements_; // set of elements.
  NumericalRev<int> num_elements_; // number of elements in the set.
};

class Store;
class SumLessConstant;
class SumTriggerAction;

class Atom {
 public:
  Atom(AtomIndex index) : atom_index_(index) {}
  ~Atom() {}

  void Listen(SumLessConstant* const ct) {
    sum_less_constant_constraints_.push_back(ct);
  }

  void Listen(Solver* const solver, SumTriggerAction* const ct) {
    sum_trigger_actions_constraints_.Insert(solver, ct);
  }

  void StopListening(Solver* const solver, SumTriggerAction* const ct) {
    sum_trigger_actions_constraints_.RemoveElement(solver, ct);
  }

  void AddFlipAction(AtomIndex action) {
    actions_.push_back(action);
  }

  void Flip(Store* const store);

  bool IsFlipped() const {
    return flipped_.Switched();
  }

 private:
  const AtomIndex atom_index_;
  std::vector<SumLessConstant*> sum_less_constant_constraints_;
  UnorderedRevArray<SumTriggerAction*> sum_trigger_actions_constraints_;
  std::vector<AtomIndex> actions_;
  RevSwitch flipped_;
};

class Store : public Constraint {
 public:
  Store(Solver* const solver) : Constraint(solver) {}

  ~Store() {}

  AtomIndex TrueIndex(IntVar* const var) {
    int raw_index = indices_.Add(var);
    if (raw_index >= true_atoms_.size()) {
      true_atoms_.resize(raw_index);
      false_atoms_.resize(raw_index);
    }
    return 1 + AtomIndex(raw_index);
  }

  AtomIndex FalseIndex(IntVar* const var) {
    return -TrueIndex(var);
  }

  void VariableBound(int index) {
    if (indices_[index]->Min() == 0) {
      Flip(AtomIndex(-1 - index));
    } else {
      Flip(AtomIndex(1 + index));
    }
  }

  void Listen(AtomIndex atom, SumLessConstant* const ct) {
    FindAtom(atom)->Listen(ct);
  }

  void Listen(AtomIndex atom, SumTriggerAction* const ct) {
    FindAtom(atom)->Listen(solver(), ct);
  }

  void StopListening(AtomIndex atom, SumTriggerAction* const ct) {
    FindAtom(atom)->StopListening(solver(), ct);
  }

  void Flip(AtomIndex atom) {
    if (atom == kFailAtom || IsFlipped(-atom)) {
      solver()->Fail();
    } else {
      FindAtom(atom)->Flip(this);
    }
  }

  bool IsFlipped(AtomIndex atom) {
    if (atom == kFailAtom) {
      return false;
    } else {
      return FindAtom(atom)->IsFlipped();
    }
  }

  virtual void Post() {
    for (int i = 0; i < indices_.size(); ++i) {
      Demon* const d = MakeConstraintDemon1(solver(),
                                            this,
                                            &Store::VariableBound,
                                            "VariableBound",
                                            i);
      indices_[i]->WhenDomain(d);
    }
  }

  virtual void InitialPropagate() {
    for (int i = 0; i < indices_.size(); ++i) {
      if (indices_[i]->Bound()) {
        VariableBound(i);
      }
    }
  }

 private:
  Atom* FindAtom(AtomIndex atom) const {
    CHECK_NE(atom, kFailAtom);
    if (atom > kFailAtom) {
      return true_atoms_[atom.value() - 1];
    } else {
      return false_atoms_[-1 - atom.value()];
    }
  }

  VectorMap<IntVar*> indices_;
  std::vector<Atom*> true_atoms_;
  std::vector<Atom*> false_atoms_;
};

class SumLessConstant {
 public:
  SumLessConstant(std::vector<AtomIndex>& vars, int constant)
      : vars_(vars),
        constant_(constant),
        sum_(0) {}

  ~SumLessConstant() {}

  void Post(Store* const store) {
    for (int i = 0; i < vars_.size(); ++i) {
      store->Listen(vars_[i], this);
    }
  }

  void Flip(Store* const store, AtomIndex index) {
    sum_.Incr(store->solver());
    if (sum_.Value() > constant_) {
      store->solver()->Fail();
    } else if (sum_.Value() == constant_) {
      UnflipAllPending(store);
    }
  }

  void UnflipAllPending(Store* const store) {
    int count = 0;
    for (int i = 0; i < vars_.size(); ++i) {
      if (!store->IsFlipped(vars_[i])) {
        count++;
        store->Flip(-vars_[i]);
      }
    }
    // Check count.
  }

 private:
  std::vector<AtomIndex> vars_;
  const int constant_;
  NumericalRev<int> sum_;
};

class SumTriggerAction {
 public:
  SumTriggerAction(std::vector<AtomIndex>& vars,
                   int constant,
                   std::vector<AtomIndex>& actions)
      : vars_(vars),
        constant_(constant),
        actions_(actions),
        sum_(0) {}

  ~SumTriggerAction() {}

  void Post(Store* const store) {
    for (int i = 0; i < vars_.size(); ++i) {
      store->Listen(vars_[i], this);
    }
  }

  void Flip(Store* const store, AtomIndex index) {
    sum_.Incr(store->solver());
    if (sum_.Value() >= constant_) {
      StopListening(store);
      FlipAllAction(store);
    }
  }

  void StopListening(Store* const store) {
    for (int i = 0; i < vars_.size(); ++i) {
      store->StopListening(vars_[i], this);
    }
  }

  void FlipAllAction(Store* const store) {
    for (int i = 0; i < actions_.size(); ++i) {
      store->Flip(actions_[i]);
    }
  }

 private:
  std::vector<AtomIndex> vars_;
  const int constant_;
  std::vector<AtomIndex> actions_;
  NumericalRev<int> sum_;
};

void Atom::Flip(Store* const store) {
  CHECK(!flipped_.Switched());
  flipped_.Switch(store->solver());
  for (int i = 0; i < actions_.size(); ++i) {
    store->Flip(actions_[i]);
  }
  for (int i = 0; i < sum_less_constant_constraints_.size(); ++i) {
    sum_less_constant_constraints_[i]->Flip(store, atom_index_);
  }
  for (int i = 0; i < sum_trigger_actions_constraints_.Size(); ++i) {
    sum_trigger_actions_constraints_[i]->Flip(store, atom_index_);
  }
}
}  // namespace

}  // namespace operations_research
<|endoftext|>


<commit_before>// rbOOmit: An implementation of the Certified Reduced Basis method.
// Copyright (C) 2009, 2010 David J. Knezevic

// This file is part of rbOOmit.

// rbOOmit is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// rbOOmit 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
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "transient_rb_param_subdomain_tree.h"
#include "transient_rb_param_subdomain_node.h"
#include "getpot.h"

TransientRBParamSubdomainTree::TransientRBParamSubdomainTree
  (TransientRBSystem& rb_system_in, const std::string& parameters_filename)
  : Parent(rb_system_in, parameters_filename),
    conserv_factor(-1.),
    use_delta_N_in_h_stage(false)
{
  // Read in properties from parameters_filename
  GetPot infile(parameters_filename);

  conserv_factor = infile("conserv_factor",conserv_factor);
  libmesh_assert(conserv_factor >= 1.);

  // Set the boolean that indicates whether we use delta_N or the POD_tol
  // in order to determine how many basis functions we add in the h-stage
  // refinement for a time-dependent problem.
  use_delta_N_in_h_stage = infile("use_delta_N_in_h_stage",use_delta_N_in_h_stage);

  libMesh::out << std::endl << "TransientRBParamSubdomainTree parameters:" << std::endl;
  libMesh::out << "Conservativity factor: " << conserv_factor << std::endl;
  libMesh::out << "Using delta_N in h-stage: " << use_delta_N_in_h_stage << std::endl;
  libMesh::out << std::endl;
}

void TransientRBParamSubdomainTree::build_root_node()
{
  libmesh_assert(root_node == NULL);
  root_node = new TransientRBParamSubdomainNode(*this, _rb_system.get_current_parameters());
}
<commit_msg>Adding missing namespace libMesh<commit_after>// rbOOmit: An implementation of the Certified Reduced Basis method.
// Copyright (C) 2009, 2010 David J. Knezevic

// This file is part of rbOOmit.

// rbOOmit is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// rbOOmit 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
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "transient_rb_param_subdomain_tree.h"
#include "transient_rb_param_subdomain_node.h"
#include "getpot.h"

namespace libMesh
{

TransientRBParamSubdomainTree::TransientRBParamSubdomainTree
  (TransientRBSystem& rb_system_in, const std::string& parameters_filename)
  : Parent(rb_system_in, parameters_filename),
    conserv_factor(-1.),
    use_delta_N_in_h_stage(false)
{
  // Read in properties from parameters_filename
  GetPot infile(parameters_filename);

  conserv_factor = infile("conserv_factor",conserv_factor);
  libmesh_assert(conserv_factor >= 1.);

  // Set the boolean that indicates whether we use delta_N or the POD_tol
  // in order to determine how many basis functions we add in the h-stage
  // refinement for a time-dependent problem.
  use_delta_N_in_h_stage = infile("use_delta_N_in_h_stage",use_delta_N_in_h_stage);

  libMesh::out << std::endl << "TransientRBParamSubdomainTree parameters:" << std::endl;
  libMesh::out << "Conservativity factor: " << conserv_factor << std::endl;
  libMesh::out << "Using delta_N in h-stage: " << use_delta_N_in_h_stage << std::endl;
  libMesh::out << std::endl;
}

void TransientRBParamSubdomainTree::build_root_node()
{
  libmesh_assert(root_node == NULL);
  root_node = new TransientRBParamSubdomainNode(*this, _rb_system.get_current_parameters());
}

} // namespace libMesh
<|endoftext|>


<commit_before>#ifndef __STOUT_OPTION_HPP__
#define __STOUT_OPTION_HPP__

#include <assert.h>

template <typename T>
class Option
{
public:
  static Option<T> none()
  {
    return Option<T>(NONE);
  }

  static Option<T> some(const T& t)
  {
    return Option<T>(SOME, new T(t));
  }

  Option() : state(NONE), t(NULL) {}

  Option(const T& _t) : state(SOME), t(new T(_t)) {}

  Option(const Option<T>& that)
  {
    state = that.state;
    if (that.t != NULL) {
      t = new T(*that.t);
    } else {
      t = NULL;
    }
  }

  ~Option()
  {
    delete t;
  }

  Option<T>& operator = (const Option<T>& that)
  {
    if (this != &that) {
      delete t;
      state = that.state;
      if (that.t != NULL) {
        t = new T(*that.t);
      } else {
        t = NULL;
      }
    }

    return *this;
  }

  bool operator == (const Option<T>& that) const
  {
    return (state == NONE && that.state == NONE) ||
      (state == SOME && that.state == SOME && *t == *that.t);
  }

  bool operator != (const Option<T>& that) const
  {
    return !operator == (that);
  }

  bool isSome() const { return state == SOME; }
  bool isNone() const { return state == NONE; }

  T get() const { assert(state == SOME); return *t; }

private:
  enum State {
    SOME,
    NONE,
  };

  Option(State _state, T* _t = NULL)
    : state(_state), t(_t) {}

  State state;
  T* t;
};

#endif // __STOUT_OPTION_HPP__
<commit_msg>Added an optional default value for Option::get().<commit_after>#ifndef __STOUT_OPTION_HPP__
#define __STOUT_OPTION_HPP__

#include <assert.h>

template <typename T>
class Option
{
public:
  static Option<T> none()
  {
    return Option<T>(NONE);
  }

  static Option<T> some(const T& t)
  {
    return Option<T>(SOME, new T(t));
  }

  Option() : state(NONE), t(NULL) {}

  Option(const T& _t) : state(SOME), t(new T(_t)) {}

  Option(const Option<T>& that)
  {
    state = that.state;
    if (that.t != NULL) {
      t = new T(*that.t);
    } else {
      t = NULL;
    }
  }

  ~Option()
  {
    delete t;
  }

  Option<T>& operator = (const Option<T>& that)
  {
    if (this != &that) {
      delete t;
      state = that.state;
      if (that.t != NULL) {
        t = new T(*that.t);
      } else {
        t = NULL;
      }
    }

    return *this;
  }

  bool operator == (const Option<T>& that) const
  {
    return (state == NONE && that.state == NONE) ||
      (state == SOME && that.state == SOME && *t == *that.t);
  }

  bool operator != (const Option<T>& that) const
  {
    return !operator == (that);
  }

  bool isSome() const { return state == SOME; }
  bool isNone() const { return state == NONE; }

  T get() const { assert(state == SOME); return *t; }

  T get(const T& _t) const { return state == NONE ? _t : *t; }

private:
  enum State {
    SOME,
    NONE,
  };

  Option(State _state, T* _t = NULL)
    : state(_state), t(_t) {}

  State state;
  T* t;
};

#endif // __STOUT_OPTION_HPP__
<|endoftext|>


<commit_before>//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines NoReturnFunctionChecker, which evaluates functions that do not
// return to the caller.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/StringSwitch.h"
#include <cstdarg>

using namespace clang;
using namespace ento;

namespace {

class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>,
                                                check::PostObjCMessage > {
public:
  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
  void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const;
};

}

void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE,
                                            CheckerContext &C) const {
  const ProgramState *state = C.getState();
  const Expr *Callee = CE->getCallee();

  bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();

  if (!BuildSinks) {
    SVal L = state->getSVal(Callee);
    const FunctionDecl *FD = L.getAsFunctionDecl();
    if (!FD)
      return;

    if (FD->getAttr<AnalyzerNoReturnAttr>())
      BuildSinks = true;
    else if (const IdentifierInfo *II = FD->getIdentifier()) {
      // HACK: Some functions are not marked noreturn, and don't return.
      //  Here are a few hardwired ones.  If this takes too long, we can
      //  potentially cache these results.
      BuildSinks
        = llvm::StringSwitch<bool>(StringRef(II->getName()))
            .Case("exit", true)
            .Case("panic", true)
            .Case("error", true)
            .Case("Assert", true)
            // FIXME: This is just a wrapper around throwing an exception.
            //  Eventually inter-procedural analysis should handle this easily.
            .Case("ziperr", true)
            .Case("assfail", true)
            .Case("db_error", true)
            .Case("__assert", true)
            .Case("__assert_rtn", true)
            .Case("__assert_fail", true)
            .Case("dtrace_assfail", true)
            .Case("yy_fatal_error", true)
            .Case("_XCAssertionFailureHandler", true)
            .Case("_DTAssertionFailureHandler", true)
            .Case("_TSAssertionFailureHandler", true)
            .Default(false);
    }
  }

  if (BuildSinks)
    C.generateSink(CE);
}

static bool END_WITH_NULL isMultiArgSelector(Selector Sel, ...) {
  va_list argp;
  va_start(argp, Sel);

  unsigned Slot = 0;
  const char *Arg;
  while ((Arg = va_arg(argp, const char *))) {
    if (!Sel.getNameForSlot(Slot).equals(Arg))
      break; // still need to va_end!
    ++Slot;
  }

  va_end(argp);

  // We only succeeded if we made it to the end of the argument list.
  return (Arg == NULL);
}

void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg,
                                                   CheckerContext &C) const {
  // HACK: This entire check is to handle two messages in the Cocoa frameworks:
  // -[NSAssertionHandler
  //    handleFailureInMethod:object:file:lineNumber:description:]
  // -[NSAssertionHandler
  //    handleFailureInFunction:file:lineNumber:description:]
  // Eventually these should be annotated with __attribute__((noreturn)).
  // Because ObjC messages use dynamic dispatch, it is not generally safe to
  // assume certain methods can't return. In cases where it is definitely valid,
  // see if you can mark the methods noreturn or analyzer_noreturn instead of
  // adding more explicit checks to this method.

  if (!Msg.isInstanceMessage())
    return;

  const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface();
  if (!Receiver)
    return;
  if (!Receiver->getIdentifier()->isStr("NSAssertionHandler"))
    return;

  Selector Sel = Msg.getSelector();
  switch (Sel.getNumArgs()) {
  default:
    return;
  case 4:
    if (!isMultiArgSelector(Sel, "handleFailureInFunction", "file",
                            "lineNumber", "description", NULL))
      return;
    break;
  case 5:
    if (!isMultiArgSelector(Sel, "handleFailureInMethod", "object", "file",
                            "lineNumber", "description", NULL))
      return;
    break;
  }

  // If we got here, it's one of the messages we care about.
  C.generateSink();
}

void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
  mgr.registerChecker<NoReturnFunctionChecker>();
}
<commit_msg>[analyzer] Fix varargs helper to only use POD types even for named arguments. Thanks, Joerg.<commit_after>//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines NoReturnFunctionChecker, which evaluates functions that do not
// return to the caller.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/StringSwitch.h"
#include <cstdarg>

using namespace clang;
using namespace ento;

namespace {

class NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>,
                                                check::PostObjCMessage > {
public:
  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
  void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const;
};

}

void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE,
                                            CheckerContext &C) const {
  const ProgramState *state = C.getState();
  const Expr *Callee = CE->getCallee();

  bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();

  if (!BuildSinks) {
    SVal L = state->getSVal(Callee);
    const FunctionDecl *FD = L.getAsFunctionDecl();
    if (!FD)
      return;

    if (FD->getAttr<AnalyzerNoReturnAttr>())
      BuildSinks = true;
    else if (const IdentifierInfo *II = FD->getIdentifier()) {
      // HACK: Some functions are not marked noreturn, and don't return.
      //  Here are a few hardwired ones.  If this takes too long, we can
      //  potentially cache these results.
      BuildSinks
        = llvm::StringSwitch<bool>(StringRef(II->getName()))
            .Case("exit", true)
            .Case("panic", true)
            .Case("error", true)
            .Case("Assert", true)
            // FIXME: This is just a wrapper around throwing an exception.
            //  Eventually inter-procedural analysis should handle this easily.
            .Case("ziperr", true)
            .Case("assfail", true)
            .Case("db_error", true)
            .Case("__assert", true)
            .Case("__assert_rtn", true)
            .Case("__assert_fail", true)
            .Case("dtrace_assfail", true)
            .Case("yy_fatal_error", true)
            .Case("_XCAssertionFailureHandler", true)
            .Case("_DTAssertionFailureHandler", true)
            .Case("_TSAssertionFailureHandler", true)
            .Default(false);
    }
  }

  if (BuildSinks)
    C.generateSink(CE);
}

static bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) {
  va_list argp;
  va_start(argp, Sel);

  unsigned Slot = 0;
  const char *Arg;
  while ((Arg = va_arg(argp, const char *))) {
    if (!Sel->getNameForSlot(Slot).equals(Arg))
      break; // still need to va_end!
    ++Slot;
  }

  va_end(argp);

  // We only succeeded if we made it to the end of the argument list.
  return (Arg == NULL);
}

void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg,
                                                   CheckerContext &C) const {
  // HACK: This entire check is to handle two messages in the Cocoa frameworks:
  // -[NSAssertionHandler
  //    handleFailureInMethod:object:file:lineNumber:description:]
  // -[NSAssertionHandler
  //    handleFailureInFunction:file:lineNumber:description:]
  // Eventually these should be annotated with __attribute__((noreturn)).
  // Because ObjC messages use dynamic dispatch, it is not generally safe to
  // assume certain methods can't return. In cases where it is definitely valid,
  // see if you can mark the methods noreturn or analyzer_noreturn instead of
  // adding more explicit checks to this method.

  if (!Msg.isInstanceMessage())
    return;

  const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface();
  if (!Receiver)
    return;
  if (!Receiver->getIdentifier()->isStr("NSAssertionHandler"))
    return;

  Selector Sel = Msg.getSelector();
  switch (Sel.getNumArgs()) {
  default:
    return;
  case 4:
    if (!isMultiArgSelector(&Sel, "handleFailureInFunction", "file",
                            "lineNumber", "description", NULL))
      return;
    break;
  case 5:
    if (!isMultiArgSelector(&Sel, "handleFailureInMethod", "object", "file",
                            "lineNumber", "description", NULL))
      return;
    break;
  }

  // If we got here, it's one of the messages we care about.
  C.generateSink();
}

void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
  mgr.registerChecker<NoReturnFunctionChecker>();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2022 Google LLC.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "yggdrasil_decision_forests/learner/multitasker/multitasker.h"

#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "yggdrasil_decision_forests/learner/learner_library.h"
#include "yggdrasil_decision_forests/learner/multitasker/multitasker.pb.h"
#include "yggdrasil_decision_forests/model/multitasker/multitasker.h"
#include "yggdrasil_decision_forests/utils/concurrency.h"
#include "yggdrasil_decision_forests/utils/synchronization_primitives.h"
#include "yggdrasil_decision_forests/utils/usage.h"

namespace yggdrasil_decision_forests {
namespace model {
namespace multitasker {

MultitaskerLearner::MultitaskerLearner(
    const model::proto::TrainingConfig& training_config)
    : AbstractLearner(training_config) {}

absl::StatusOr<std::unique_ptr<AbstractModel>>
MultitaskerLearner::TrainWithStatus(
    const dataset::VerticalDataset& train_dataset,
    absl::optional<std::reference_wrapper<const dataset::VerticalDataset>>
        valid_dataset) const {
  const auto& mt_config =
      training_config().GetExtension(proto::multitasker_config);
  if (mt_config.subtasks_size() == 0) {
    return absl::InvalidArgumentError("At least one task required");
  }

  auto model = absl::make_unique<MultitaskerModel>();
  model->set_data_spec(train_dataset.data_spec());

  ASSIGN_OR_RETURN(const auto first_subtraining_config,
                   BuildSubTrainingConfig(0));
  model::proto::TrainingConfigLinking first_subtraining_config_link;
  RETURN_IF_ERROR(AbstractLearner::LinkTrainingConfig(
      first_subtraining_config, train_dataset.data_spec(),
      &first_subtraining_config_link));
  InitializeModelWithAbstractTrainingConfig(
      first_subtraining_config, first_subtraining_config_link, model.get());

  utils::concurrency::Mutex mutex;
  absl::Status status;

  const auto train_subtask = [&](const int subtask_idx) {
    ASSIGN_OR_RETURN(const auto sublearner, BuildSubLearner(subtask_idx));
    ASSIGN_OR_RETURN(auto submodel,
                     sublearner->TrainWithStatus(train_dataset, valid_dataset));
    utils::concurrency::MutexLock lock(&mutex);
    model->models_.push_back(std::move(submodel));
    return absl::OkStatus();
  };

  const auto train_subtask_nostatus = [&](const int subtask_idx) {
    {
      utils::concurrency::MutexLock lock(&mutex);
      if (!status.ok()) {
        return;
      }
    }
    const auto substatus = train_subtask(subtask_idx);
    utils::concurrency::MutexLock lock(&mutex);
    if (!substatus.ok()) {
      status.Update(substatus);
    }
  };

  {
    utils::concurrency::ThreadPool pool("multitasker",
                                        deployment().num_threads());
    pool.StartWorkers();
    for (int subtask_idx = 0; subtask_idx < mt_config.subtasks_size();
         subtask_idx++) {
      pool.Schedule([train_subtask_nostatus, subtask_idx]() {
        train_subtask_nostatus(subtask_idx);
      });
    }
  }

  RETURN_IF_ERROR(status);

  return model;
}

absl::Status MultitaskerLearner::SetHyperParameters(
    const model::proto::GenericHyperParameters& generic_hyper_params) {
  generic_hyper_params_ = generic_hyper_params;
  return absl::OkStatus();
}

absl::StatusOr<model::proto::TrainingConfig>
MultitaskerLearner::BuildSubTrainingConfig(const int learner_idx) const {
  const auto& mt_config =
      training_config().GetExtension(proto::multitasker_config);

  model::proto::TrainingConfig sub_learner_config = mt_config.base_learner();
  sub_learner_config.MergeFrom(mt_config.subtasks(learner_idx).train_config());

  if (training_config().has_maximum_training_duration_seconds() &&
      !sub_learner_config.has_maximum_training_duration_seconds()) {
    sub_learner_config.set_maximum_training_duration_seconds(
        training_config().maximum_training_duration_seconds());
  }

  if (training_config().has_maximum_model_size_in_memory_in_bytes() &&
      !sub_learner_config.has_maximum_model_size_in_memory_in_bytes()) {
    sub_learner_config.set_maximum_model_size_in_memory_in_bytes(
        training_config().maximum_model_size_in_memory_in_bytes());
  }
  return sub_learner_config;
}

absl::StatusOr<std::unique_ptr<AbstractLearner>>
MultitaskerLearner::BuildSubLearner(const int learner_idx) const {
  const auto& mt_config =
      training_config().GetExtension(proto::multitasker_config);

  ASSIGN_OR_RETURN(const auto sub_learner_config,
                   BuildSubTrainingConfig(learner_idx));

  // Build sub-model learner.
  std::unique_ptr<AbstractLearner> sub_learner;
  RETURN_IF_ERROR(GetLearner(sub_learner_config, &sub_learner));
  *sub_learner->mutable_deployment() = mt_config.base_learner_deployment();
  RETURN_IF_ERROR(sub_learner->SetHyperParameters(generic_hyper_params_));
  return sub_learner;
}

absl::StatusOr<model::proto::GenericHyperParameterSpecification>
MultitaskerLearner::GetGenericHyperParameterSpecification() const {
  ASSIGN_OR_RETURN(auto sub_learner, BuildSubLearner(0));
  return sub_learner->GetGenericHyperParameterSpecification();
}

}  // namespace multitasker
}  // namespace model
}  // namespace yggdrasil_decision_forests
<commit_msg>Add definition for MultitaskerLearner::kRegisteredName<commit_after>/*
 * Copyright 2022 Google LLC.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "yggdrasil_decision_forests/learner/multitasker/multitasker.h"

#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "yggdrasil_decision_forests/learner/learner_library.h"
#include "yggdrasil_decision_forests/learner/multitasker/multitasker.pb.h"
#include "yggdrasil_decision_forests/model/multitasker/multitasker.h"
#include "yggdrasil_decision_forests/utils/concurrency.h"
#include "yggdrasil_decision_forests/utils/synchronization_primitives.h"
#include "yggdrasil_decision_forests/utils/usage.h"

namespace yggdrasil_decision_forests {
namespace model {
namespace multitasker {

constexpr char MultitaskerLearner::kRegisteredName[];

MultitaskerLearner::MultitaskerLearner(
    const model::proto::TrainingConfig& training_config)
    : AbstractLearner(training_config) {}

absl::StatusOr<std::unique_ptr<AbstractModel>>
MultitaskerLearner::TrainWithStatus(
    const dataset::VerticalDataset& train_dataset,
    absl::optional<std::reference_wrapper<const dataset::VerticalDataset>>
        valid_dataset) const {
  const auto& mt_config =
      training_config().GetExtension(proto::multitasker_config);
  if (mt_config.subtasks_size() == 0) {
    return absl::InvalidArgumentError("At least one task required");
  }

  auto model = absl::make_unique<MultitaskerModel>();
  model->set_data_spec(train_dataset.data_spec());

  ASSIGN_OR_RETURN(const auto first_subtraining_config,
                   BuildSubTrainingConfig(0));
  model::proto::TrainingConfigLinking first_subtraining_config_link;
  RETURN_IF_ERROR(AbstractLearner::LinkTrainingConfig(
      first_subtraining_config, train_dataset.data_spec(),
      &first_subtraining_config_link));
  InitializeModelWithAbstractTrainingConfig(
      first_subtraining_config, first_subtraining_config_link, model.get());

  utils::concurrency::Mutex mutex;
  absl::Status status;

  const auto train_subtask = [&](const int subtask_idx) {
    ASSIGN_OR_RETURN(const auto sublearner, BuildSubLearner(subtask_idx));
    ASSIGN_OR_RETURN(auto submodel,
                     sublearner->TrainWithStatus(train_dataset, valid_dataset));
    utils::concurrency::MutexLock lock(&mutex);
    model->models_.push_back(std::move(submodel));
    return absl::OkStatus();
  };

  const auto train_subtask_nostatus = [&](const int subtask_idx) {
    {
      utils::concurrency::MutexLock lock(&mutex);
      if (!status.ok()) {
        return;
      }
    }
    const auto substatus = train_subtask(subtask_idx);
    utils::concurrency::MutexLock lock(&mutex);
    if (!substatus.ok()) {
      status.Update(substatus);
    }
  };

  {
    utils::concurrency::ThreadPool pool("multitasker",
                                        deployment().num_threads());
    pool.StartWorkers();
    for (int subtask_idx = 0; subtask_idx < mt_config.subtasks_size();
         subtask_idx++) {
      pool.Schedule([train_subtask_nostatus, subtask_idx]() {
        train_subtask_nostatus(subtask_idx);
      });
    }
  }

  RETURN_IF_ERROR(status);

  return model;
}

absl::Status MultitaskerLearner::SetHyperParameters(
    const model::proto::GenericHyperParameters& generic_hyper_params) {
  generic_hyper_params_ = generic_hyper_params;
  return absl::OkStatus();
}

absl::StatusOr<model::proto::TrainingConfig>
MultitaskerLearner::BuildSubTrainingConfig(const int learner_idx) const {
  const auto& mt_config =
      training_config().GetExtension(proto::multitasker_config);

  model::proto::TrainingConfig sub_learner_config = mt_config.base_learner();
  sub_learner_config.MergeFrom(mt_config.subtasks(learner_idx).train_config());

  if (training_config().has_maximum_training_duration_seconds() &&
      !sub_learner_config.has_maximum_training_duration_seconds()) {
    sub_learner_config.set_maximum_training_duration_seconds(
        training_config().maximum_training_duration_seconds());
  }

  if (training_config().has_maximum_model_size_in_memory_in_bytes() &&
      !sub_learner_config.has_maximum_model_size_in_memory_in_bytes()) {
    sub_learner_config.set_maximum_model_size_in_memory_in_bytes(
        training_config().maximum_model_size_in_memory_in_bytes());
  }
  return sub_learner_config;
}

absl::StatusOr<std::unique_ptr<AbstractLearner>>
MultitaskerLearner::BuildSubLearner(const int learner_idx) const {
  const auto& mt_config =
      training_config().GetExtension(proto::multitasker_config);

  ASSIGN_OR_RETURN(const auto sub_learner_config,
                   BuildSubTrainingConfig(learner_idx));

  // Build sub-model learner.
  std::unique_ptr<AbstractLearner> sub_learner;
  RETURN_IF_ERROR(GetLearner(sub_learner_config, &sub_learner));
  *sub_learner->mutable_deployment() = mt_config.base_learner_deployment();
  RETURN_IF_ERROR(sub_learner->SetHyperParameters(generic_hyper_params_));
  return sub_learner;
}

absl::StatusOr<model::proto::GenericHyperParameterSpecification>
MultitaskerLearner::GetGenericHyperParameterSpecification() const {
  ASSIGN_OR_RETURN(auto sub_learner, BuildSubLearner(0));
  return sub_learner->GetGenericHyperParameterSpecification();
}

}  // namespace multitasker
}  // namespace model
}  // namespace yggdrasil_decision_forests
<|endoftext|>


<commit_before>// Copyright (c) 2015 The Brick Authors.

#include "brick/download_client.h"

#include <ctime>
#include <fstream>
#include <include/cef_parser.h>

#include "include/base/cef_logging.h"
#include "brick/brick_types.h"
#include "brick/client_handler.h"
#include "brick/platform_util.h"
#include "brick/helper.h"

namespace {
  const char kTmpSuffix[] = ".brdown";

}  // namespace

DownloadClient::DownloadClient(const std::string& id, const std::string& path, const std::string& name)
    : id_ (id),
      file_path_ (path),
      file_name_ (name),
      last_percent_ (0) {

  CEF_REQUIRE_UI_THREAD();
  tmp_file_path_ = path + kTmpSuffix;
  tmp_file_.open(tmp_file_path_, std::ofstream::binary);
}

void
DownloadClient::Detach() {
  CEF_REQUIRE_UI_THREAD();

  tmp_file_.close();
}

void
DownloadClient::OnRequestComplete(CefRefPtr<CefURLRequest> request) {
  CEF_REQUIRE_UI_THREAD();

  tmp_file_.close();
  DownloadClientStatus status = DC_STATUS_FAILED;
  DownloadClientReason reason = DC_REASON_UNKNOWN;
  if (request->GetRequestError() == ERR_NONE && request->GetResponse()->GetStatus() == 200) {
    if (rename(tmp_file_path_.c_str(), file_path_.c_str()) == 0) {
      status = DC_STATUS_SUCCESS;
      reason = DC_REASON_NONE;
    } else {
      reason = DC_REASON_UNKNOWN;
      LOG(WARNING) << "Can't rename downloaded file. ID: " << id_ << ". "
                                << "Tmp path: " << tmp_file_path_ << "."
                                << "Result path: " << file_path_ ;
    }
  } else if (request->GetRequestError() == ERR_ABORTED) {
    reason = DC_REASON_ABORTED;
    unlink(tmp_file_path_.c_str());
    LOG(INFO) << "Download aborted. ID: " << id_ << ". "
                 << "Url: " << request->GetRequest()->GetURL().ToString();
  } else {
    reason = DC_REASON_HTTP;
    LOG(WARNING) << "Download failed. ID: " << id_ << ". "
                 << "Url: " << request->GetRequest()->GetURL().ToString() << "."
                 << "Reason: " << request_util::GetErrorString(request->GetRequestError());
  }

  DownloadCompleteEvent e(id_, file_path_, status, reason);
  EventBus::FireEvent(e);
}

void
DownloadClient::OnDownloadProgress(CefRefPtr<CefURLRequest> request, int64 current, int64 total) {
  if (total <= 0)
    return;
  // ToDo: what should be without total size?
  // Send only "current" w/o "percent" and "total"? How often?

  int progress = static_cast<int>(current * 100.0 / total);
  if (progress == last_percent_) {
    return;
  }

  last_percent_ = progress;
  DownloadProgressEvent e(id_, progress, current, total);
  EventBus::FireEvent(e);
}

void
DownloadClient::OnDownloadData(CefRefPtr<CefURLRequest> request,
                             const void* data,
                             size_t data_length) {

  CEF_REQUIRE_UI_THREAD();
  tmp_file_.write(static_cast<const char*>(data), data_length);
}

// static methods

CefRefPtr<CefURLRequest>
DownloadClient::CreateRequest(
    const std::string& id,
    const std::string& url,
    const std::string& path,
    const std::string& name) {

  CEF_REQUIRE_UI_THREAD();

// ToDo: What we must todo in this case?
//  if (platform_util::IsPathExists(path + kTmpSuffix))
//    return NULL;  // Probably we already started downloading

  CefRefPtr<CefRequest> request = CefRequest::Create();
  request->SetURL(url);
  request->SetMethod("GET");
  request->SetFlags(UR_FLAG_ALLOW_CACHED_CREDENTIALS|UR_FLAG_NO_RETRY_ON_5XX|UR_FLAG_STOP_ON_REDIRECT|UR_FLAG_SKIP_CACHE);

  // Create and start the new CefURLRequest.
  return CefURLRequest::Create(request, new DownloadClient(id, path, name), NULL);
}

DownloadClientDialogCallback::DownloadClientDialogCallback(std::string url)
  : url_ (url) {

}

void
DownloadClientDialogCallback::Continue(
    int selected_accept_filter,
    const std::vector<CefString>& file_paths) {

  if (file_paths.empty())
    return;

  std::string file = file_paths.cbegin()->ToString();
  std::string filename = helper::GetFileName(file);
  std::string id = std::to_string(helper::HashString(url_ + file));

  CefRefPtr<CefURLRequest> request = DownloadClient::CreateRequest(
      id,
      url_,
      file,
      filename
  );

  if (request.get()) {
    ClientHandler::GetInstance()->RegisterDownload(id , request);
    DownloadStartEvent e(id, filename, file, url_, std::time(nullptr));
    EventBus::FireEvent(e);
  }
}<commit_msg>DownloadClient создает папочку перед запуском скачивания. Шанс не велик, но на всякий случай.<commit_after>// Copyright (c) 2015 The Brick Authors.

#include "brick/download_client.h"

#include <ctime>
#include <fstream>
#include <include/cef_parser.h>

#include "include/base/cef_logging.h"
#include "brick/brick_types.h"
#include "brick/client_handler.h"
#include "brick/platform_util.h"
#include "brick/helper.h"

namespace {
  const char kTmpSuffix[] = ".brdown";

}  // namespace

DownloadClient::DownloadClient(const std::string& id, const std::string& path, const std::string& name)
    : id_ (id),
      file_path_ (path),
      file_name_ (name),
      last_percent_ (0) {

  CEF_REQUIRE_UI_THREAD();
  tmp_file_path_ = path + kTmpSuffix;
  tmp_file_.open(tmp_file_path_, std::ofstream::binary);
}

void
DownloadClient::Detach() {
  CEF_REQUIRE_UI_THREAD();

  tmp_file_.close();
}

void
DownloadClient::OnRequestComplete(CefRefPtr<CefURLRequest> request) {
  CEF_REQUIRE_UI_THREAD();

  tmp_file_.close();
  DownloadClientStatus status = DC_STATUS_FAILED;
  DownloadClientReason reason = DC_REASON_UNKNOWN;
  if (request->GetRequestError() == ERR_NONE && request->GetResponse()->GetStatus() == 200) {
    if (rename(tmp_file_path_.c_str(), file_path_.c_str()) == 0) {
      status = DC_STATUS_SUCCESS;
      reason = DC_REASON_NONE;
    } else {
      reason = DC_REASON_UNKNOWN;
      LOG(WARNING) << "Can't rename downloaded file. ID: " << id_ << ". "
                                << "Tmp path: " << tmp_file_path_ << "."
                                << "Result path: " << file_path_ ;
    }
  } else if (request->GetRequestError() == ERR_ABORTED) {
    reason = DC_REASON_ABORTED;
    unlink(tmp_file_path_.c_str());
    LOG(INFO) << "Download aborted. ID: " << id_ << ". "
                 << "Url: " << request->GetRequest()->GetURL().ToString();
  } else {
    reason = DC_REASON_HTTP;
    LOG(WARNING) << "Download failed. ID: " << id_ << ". "
                 << "Url: " << request->GetRequest()->GetURL().ToString() << "."
                 << "Reason: " << request_util::GetErrorString(request->GetRequestError());
  }

  DownloadCompleteEvent e(id_, file_path_, status, reason);
  EventBus::FireEvent(e);
}

void
DownloadClient::OnDownloadProgress(CefRefPtr<CefURLRequest> request, int64 current, int64 total) {
  if (total <= 0)
    return;
  // ToDo: what should be without total size?
  // Send only "current" w/o "percent" and "total"? How often?

  int progress = static_cast<int>(current * 100.0 / total);
  if (progress == last_percent_) {
    return;
  }

  last_percent_ = progress;
  DownloadProgressEvent e(id_, progress, current, total);
  EventBus::FireEvent(e);
}

void
DownloadClient::OnDownloadData(CefRefPtr<CefURLRequest> request,
                             const void* data,
                             size_t data_length) {

  CEF_REQUIRE_UI_THREAD();
  tmp_file_.write(static_cast<const char*>(data), data_length);
}

// static methods

CefRefPtr<CefURLRequest>
DownloadClient::CreateRequest(
    const std::string& id,
    const std::string& url,
    const std::string& path,
    const std::string& name) {

  CEF_REQUIRE_UI_THREAD();

  if (!platform_util::MakeDirectory(helper::BaseDir(path)))
    return NULL;

// ToDo: What we must todo in this case?
//  if (platform_util::IsPathExists(path + kTmpSuffix))
//    return NULL;  // Probably we already started downloading

  CefRefPtr<CefRequest> request = CefRequest::Create();
  request->SetURL(url);
  request->SetMethod("GET");
  request->SetFlags(UR_FLAG_ALLOW_CACHED_CREDENTIALS|UR_FLAG_NO_RETRY_ON_5XX|UR_FLAG_STOP_ON_REDIRECT|UR_FLAG_SKIP_CACHE);

  // Create and start the new CefURLRequest.
  return CefURLRequest::Create(request, new DownloadClient(id, path, name), NULL);
}

DownloadClientDialogCallback::DownloadClientDialogCallback(std::string url)
  : url_ (url) {

}

void
DownloadClientDialogCallback::Continue(
    int selected_accept_filter,
    const std::vector<CefString>& file_paths) {

  if (file_paths.empty())
    return;

  std::string file = file_paths.cbegin()->ToString();
  std::string filename = helper::GetFileName(file);
  std::string id = std::to_string(helper::HashString(url_ + file));

  CefRefPtr<CefURLRequest> request = DownloadClient::CreateRequest(
      id,
      url_,
      file,
      filename
  );

  if (request.get()) {
    ClientHandler::GetInstance()->RegisterDownload(id , request);
    DownloadStartEvent e(id, filename, file, url_, std::time(nullptr));
    EventBus::FireEvent(e);
  }
}<|endoftext|>


<commit_before>/*! \file
 *
 * \brief Conversion between python and C++ (header)
 * \author Benjamin Pritchard (ben@bennyp.org)
 */

#ifndef _GUARD_CONVERT_HPP_
#define _GUARD_CONVERT_HPP_

#include "bpmodule/output/Output.hpp"

#include "bpmodule/python/Pybind11.hpp"
#include "bpmodule/python/Pybind11_stl.hpp"
#include "bpmodule/python/Types.hpp"
#include "bpmodule/python/Errors.hpp"
#include "bpmodule/exception/PythonConvertException.hpp"
#include "bpmodule/util/Mangle.hpp"

namespace bpmodule {
namespace python {

/*! \brief Convert a python object to a C++ object
 *
 * \throw bpmodule::exception::PythonConvertException on error
 *
 * \tparam T The C++ type to convert to
 * \param [in] obj The python object to convert
 */
template<typename T>
T ConvertToCpp(pybind11::object obj)
{
    using bpmodule::exception::PythonConvertException;

    //! \todo more restrictive?

    // throws if there is an issue
    //! \todo exception handling
    try {
        return obj.cast<T>();
    }
    catch(const std::exception & ex)
    {
        throw PythonConvertException("Cannot convert from python to C++: Conversion failed",
                                     GetPyClass(obj), util::DemangleCppType<T>(),
                                     "what", ex.what());
    }
    catch(...)
    {
        throw PythonConvertException("Cannot convert from python to C++: Conversion failed",
                                     GetPyClass(obj), util::DemangleCppType<T>(),
                                     "what", "unknown exception type");
    }
}

/*! \brief Convert a C++ object to a python object
 *
 * \throw bpmodule::exception::PythonConvertException on error
 *
 * \tparam T The C++ type to convert from
 * \param [in] obj The python object to convert
 */
template<typename T>
pybind11::object ConvertToPy(const T & obj)
{
    using bpmodule::exception::PythonConvertException;

    // may NOT throw if there is an issue
    try {
        pybind11::object pyobj = pybind11::cast(obj);

        //! \todo fix if this is fixed in pybind11
        if(pyobj.ptr() == nullptr)
        {
            throw PythonConvertException(detail::GetPyException(),
                                         util::DemangleCppType<T>(), "python object",
                                         "info", "Resulting object pointer is null");
        }
        else
            return pyobj;

    }
    catch (const std::exception & ex)
    {
        throw PythonConvertException(ex.what(),
                                     util::DemangleCppType<T>(), "python object",
                                     "info", "In converting from C++ to python object");
    }
    catch(...)
    {
        throw PythonConvertException("Caught unknown exception",
                                     util::DemangleCppType<T>(), "python object",
                                     "info", "In converting from C++ to python object");
    }
}

/*! \brief Convert a plain array of C++ objects to a python object
 *
 * \throw exception::PythonConvertException if the
 *        data could not be converted
 *
 * \tparam T The C++ type to convert from
 *
 * \param [in] obj An array of objects
 * \param [in] n Length of the array
 * \return Converted data as a boost python::object
 */
template<typename T>
pybind11::object ConvertToPy(const T * const obj, size_t n)
{
    std::vector<T> v(obj, obj+n);

    return ConvertToPy(v);
}

} // close namespace python
} // close namespace bpmodule

#endif
<commit_msg>fix comment<commit_after>/*! \file
 *
 * \brief Conversion between python and C++ (header)
 * \author Benjamin Pritchard (ben@bennyp.org)
 */

#ifndef _GUARD_CONVERT_HPP_
#define _GUARD_CONVERT_HPP_

#include "bpmodule/output/Output.hpp"

#include "bpmodule/python/Pybind11.hpp"
#include "bpmodule/python/Pybind11_stl.hpp"
#include "bpmodule/python/Types.hpp"
#include "bpmodule/python/Errors.hpp"
#include "bpmodule/exception/PythonConvertException.hpp"
#include "bpmodule/util/Mangle.hpp"

namespace bpmodule {
namespace python {

/*! \brief Convert a python object to a C++ object
 *
 * \throw bpmodule::exception::PythonConvertException on error
 *
 * \tparam T The C++ type to convert to
 * \param [in] obj The python object to convert
 */
template<typename T>
T ConvertToCpp(pybind11::object obj)
{
    using bpmodule::exception::PythonConvertException;

    //! \todo more restrictive?

    // throws if there is an issue
    //! \todo exception handling
    try {
        return obj.cast<T>();
    }
    catch(const std::exception & ex)
    {
        throw PythonConvertException("Cannot convert from python to C++: Conversion failed",
                                     GetPyClass(obj), util::DemangleCppType<T>(),
                                     "what", ex.what());
    }
    catch(...)
    {
        throw PythonConvertException("Cannot convert from python to C++: Conversion failed",
                                     GetPyClass(obj), util::DemangleCppType<T>(),
                                     "what", "unknown exception type");
    }
}

/*! \brief Convert a C++ object to a python object
 *
 * \throw bpmodule::exception::PythonConvertException on error
 *
 * \tparam T The C++ type to convert from
 * \param [in] obj The python object to convert
 */
template<typename T>
pybind11::object ConvertToPy(const T & obj)
{
    using bpmodule::exception::PythonConvertException;

    // may NOT throw if there is an issue
    try {
        pybind11::object pyobj = pybind11::cast(obj);

        //! \todo fix if this is fixed in pybind11
        if(pyobj.ptr() == nullptr)
        {
            throw PythonConvertException(detail::GetPyException(),
                                         util::DemangleCppType<T>(), "python object",
                                         "info", "Resulting object pointer is null");
        }
        else
            return pyobj;

    }
    catch (const std::exception & ex)
    {
        throw PythonConvertException(ex.what(),
                                     util::DemangleCppType<T>(), "python object",
                                     "info", "In converting from C++ to python object");
    }
    catch(...)
    {
        throw PythonConvertException("Caught unknown exception",
                                     util::DemangleCppType<T>(), "python object",
                                     "info", "In converting from C++ to python object");
    }
}

/*! \brief Convert a plain array of C++ objects to a python object
 *
 * \throw exception::PythonConvertException if the
 *        data could not be converted
 *
 * \tparam T The C++ type to convert from
 *
 * \param [in] obj An array of objects
 * \param [in] n Length of the array
 * \return Converted data as a python object
 */
template<typename T>
pybind11::object ConvertToPy(const T * const obj, size_t n)
{
    std::vector<T> v(obj, obj+n);

    return ConvertToPy(v);
}

} // close namespace python
} // close namespace bpmodule

#endif
<|endoftext|>