text:
// Copyright 2015-2021 Carnegie Mellon University.  See LICENSE file for terms.

#include <algorithm>
#include <iterator>
#include <cstring>
#include <sstream>
#include <wordexp.h>

#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/algorithm_ext/insert.hpp>
#include <boost/range/adaptor/map.hpp>

#include <rose.h>
#include <Sawyer/ProgressBar.h>

#include "options.hpp"
#include "util.hpp"
#include "descriptors.hpp"
#include "masm.hpp"
#include "revision.hpp"
#include "apidb.hpp"
#include "limit.hpp"
#include "semantics.hpp"
#include "path.hpp"
#include "bua.hpp"

// these next ones needed for global obj cleanup...
#include "riscops.hpp"
#include "vftable.hpp"

// for non portable unhandled exception stuff in pharos_main:
#ifdef __GNUC__
#include <cstdlib>
#include <cxxabi.h>
using namespace __cxxabiv1;
#endif

namespace boost {
namespace filesystem {
void validate(boost::any& v,
              std::vector<std::string> const & values,
              boost::filesystem::path *, int)
{
  using namespace boost::program_options;
  validators::check_first_occurrence(v);
  std::string const & s = validators::get_single_string(values);
  wordexp_t we;
  int rv = wordexp(s.c_str(), &we, (WRDE_NOCMD | WRDE_UNDEF));
  if (rv != 0 || we.we_wordc != 1) {
    throw validation_error(validation_error::invalid_option_value);
  }
  auto deleter = [](wordexp_t *w) { wordfree(w); };
  std::unique_ptr<wordexp_t, decltype(deleter)> we_guard(&we, deleter);
  v = boost::any(boost::filesystem::path(we.we_wordv[0]));
}

}}

namespace pharos {

namespace bf = boost::filesystem;

using prolog::plog;

// This is library path from the command line or the default value.
namespace {
bf::path library_path;

int global_logging_fileno = -1;
LogDestination global_logging_destination;
bool global_logging_iteractive = false;
}

bool interactive_logging() {
  return global_logging_iteractive;
}

LogDestination get_logging_destination()
{
  if (global_logging_destination) {
    return global_logging_destination;
  }
  Sawyer::Message::PrefixPtr prefix = Sawyer::Message::Prefix::instance();
  prefix = prefix->showProgramName(false);
  // It's sometimes useful to also disable the faciltiy and importance.
  //prefix = prefix->showFacilityName(Sawyer::Message::NEVER)->showImportance(false);

  if (global_logging_fileno < 0) {
    global_logging_fileno = STDOUT_FILENO;
  }
  global_logging_destination =
    Sawyer::Message::FdSink::instance(global_logging_fileno, prefix);

  return global_logging_destination;
}

// For logging options and other critically important "informational" messages.  This facility
// is meant to always be logged at level "INFO" and above.  The "WHERE" level includes optional
// messages, including the affirmative reporting of which options were detected.
Sawyer::Message::Facility olog;

// Get the message levels from Sawyer::Message::Common.
using namespace Sawyer::Message::Common;

#define DEFAULT_VERBOSITY 3
#define MAXIMUM_VERBOSITY 14

ProgOptDesc cert_standard_options() {
  namespace po = boost::program_options;

  ProgOptDesc certopt("CERT/Pharos options");

  // Don't forget to update the documentation if you change this list!
  certopt.add_options()
    ("help,h",        "display help")

    // Important options affecting lots of programs?
    ("verbose,v",
     po::value<int>()->implicit_value(DEFAULT_VERBOSITY),
     //po::value<int>()->default_value(DEFAULT_VERBOSITY),
     boost::str(boost::format("enable verbose logging (1-%d, default %d)")
                % MAXIMUM_VERBOSITY % DEFAULT_VERBOSITY).c_str())
    ("timing", po::bool_switch(), "Include duration field in log messages")
    ("batch,b", "suppress colors, progress bars, etc.")

    // Increasingly important?  And possibly going away before too much longer?
    ("allow-64bit", "allow analysis of 64-bit executables")

    // The inclusion and exclusion of specific functions (commonly used?)
    ("include-func,i",
     po::value<StrVector>(),
     "limit analysis to a specific function")
    ("exclude-func,e",
     po::value<StrVector>(),
     "exclude analysis of a specific function")

    // These are options controlling the configuration of the Pharos system.
    ("config,C",
     po::value<std::vector<bf::path>>()->composing(),
     "pharos configuration file (can be specified multiple times)")
    ("option",
     po::value<std::vector<std::string>>()->composing(),
     "configuration value specification, key1.key2=value, (can be specified multiple times)")
    ("dump-config",
     "display current active config parameters")
    ("no-user-file",
     "don't load the user's configuration file")
    ("no-site-file",
     "don't load the site's configuration file")
    ("apidb", po::value<std::vector<bf::path>>(),
     "path to sqlite or JSON file containing API and type information")
    ("library,l",
     po::value<bf::path>(),
     "specify the path to the pharos library directory")

    // Timeouts affecting the "core" analysis pass.
    ("timeout", po::value<double>(),
     "time limit (sec) for the entire analysis")
    ("per-function-timeout", po::value<double>(),
     "CPU limit (sec) per function")
    ("partitioner-timeout", po::value<double>(),
     "time limit (sec) for the partitioner")
    ("maximum-memory", po::value<double>(),
     "maximum memory (Mib) for the entire anlaysis")
    ("per-function-maximum-memory", po::value<double>(),
     "maximum memory (Mib) per function")
    ("maximum-instructions-per-block", po::value<int>(),
     "limit the number of instructions per basic block")
    ("maximum-iterations-per-function", po::value<int>(),
     "limit the number of CFG iterations per function")
    ("maximum-nodes-per-condition", po::value<int>(),
     "limit the number of tree nodes per ITE condition")

    ("threads", po::value<int>()->implicit_value(1),
     ("Number of threads to use, if this program uses threads.  "
      "A value of zero means to use all available processors.  "
      "A negative value means to use that many less than the number of available processors."))

    // Historical...  Do we even need this anymore?
    ("file,f",
     po::value<bf::path>(),
     "executable to be analyzed")
    ;
  ;

  // Don't forget to update the documentation if you change this list!

  // These are really partitioner options or maybe options that are mostly just passed along to
  // control standard ROSE behavior.
  ProgOptDesc roseopt("ROSE/Partitioner options");
  roseopt.add_options()
    ("partitioner", po::value<std::string>(),
     "specify the function parititioner")
    ("serialize", po::value<bf::path>(),
     "file which caches function partitioning information")
    ("ignore-serialize-version",
     "reject version mismatch errors when reading a serialized file")
    ("no-semantics",
     "disable semantic analysis during parititioning")
    ("pdebug",
     "enable partitioner debugging")
    ("no-executable-entry",
     "do not mark the entry point segment as executable")
    ("mark-executable",
     "mark all segments as executable during partitioning")
    ("log",
     po::value<std::string>(),
     "log facility control string")
    ("stockpart",
     "deprecated, use --parititioner=rose")
    ("rose-version",
     "output ROSE version information and exit immediately")
    ;

  // Don't forget to update the documentation if you change this list!

  certopt.add(roseopt);
  return certopt;
}

static ProgOptDesc cert_hidden_options()
{
  namespace po = boost::program_options;

  ProgOptDesc certhiddenopt("CERT Hidden options");

  // Don't forget to update the documentation if you change this list!
  certhiddenopt.add_options()
    ("maxmem", po::value<double>(),
     "the old maximum-memory")
    ("relmaxmem", po::value<double>(),
     "the old per-function-maximum-memory")
    ("ptimeout", po::value<double>(),
     "the old partitioner-timeout")
    ("blockcounterlimit", po::value<int>(),
     "the old maximum-instructions-per-block")
    ("funccounterlimit", po::value<int>(),
     "the old maximum-iterations-per-function")
    ("propagate-conditions",
     "Flag to preserve and propagate conditions when analyzing basic blocks")
    ;
  ;
  return certhiddenopt;
}

ProgOptVarMap::ProgOptVarMap(int argc, char **argv)
{
  std::copy_n(argv, argc, std::back_inserter(_args));
}

std::string ProgOptVarMap::command_line() const
{
  std::ostringstream os;
  for (auto arg : _args) {
    if (os.tellp() != 0) {
      os << ' ';
    }
    if (std::strchr(arg, ' ')) {
      os << '\'' << arg << '\'';
    }
  }
  return os.str();
}

static ProgOptVarMap parse_cert_options_internal(
  int argc, char** argv,
  ProgOptDesc partial_desc,
  const std::string & proghelptext,
  boost::optional<ProgPosOptDesc> posopt,
  Sawyer::Message::UnformattedSinkPtr destination)
{
  namespace po = boost::program_options;

  bool fileopt = false;
  if (!posopt) {
    fileopt = true;
    posopt = ProgPosOptDesc().add("file", -1);
  }

  if (destination) {
    global_logging_destination = destination;
    global_logging_fileno = -1;
  }

  // Try to locate the library root
  auto av0path = bf::path(argv[0]);
  auto prog_loc = av0path.parent_path();
  if (prog_loc.empty()) {
    // Need to look for executable in system path
    // This is unix-specific
    const char *penv = std::getenv("PATH");
    if (penv) {
      for (auto i =
             boost::make_split_iterator(penv, boost::first_finder(":", boost::is_equal()));
           i != decltype(i)(); ++i)
      {
        auto pdir = bf::path(i->begin(), i->end());
        if (bf::is_regular_file(pdir / av0path)) {
          prog_loc = bf::canonical(pdir);
          break;
        }
      }
    }
  } else {
    prog_loc = bf::canonical(prog_loc);
  }

  // This is the root of the installation path, the repository of the current working directory.
  // This path is not exposed in the API because it's only needed for etc/pharos.yaml (below).
  bf::path root_loc;
  // This is share/pharos subdirectory or the current working directory in release builds.
  // If CMakeFiles is found in development builds, it's the repository root directory.
  bf::path lib_root;

  // Assume the install root is one directory below.
  root_loc = prog_loc.parent_path();
  lib_root = root_loc / "share/pharos";

  ProgOptVarMap vm(argc, argv);;

  // Register our custom callback for determining if two expressions may be equal.
  set_may_equal_callback();

  get_logging_destination()->prefix()->showElapsedTime(false);

  // Get the options logging facility working with the standard options.
  olog.initialize("OPTI");
  olog.initStreams(get_logging_destination());
  // Initialize the global log with the appropriate filters.  Set this up before option
  // processing.
  glog.initStreams(get_logging_destination());

  ProgOptDesc od;
  od.add(partial_desc);
  od.add(cert_hidden_options());

  po::store(po::command_line_parser(argc, argv).
            options(od).positional(*posopt).run(), vm);

  bf::path program = argv[0];
  vm.config(pharos::Config::load_config(
              program.filename().native(),
              vm.count("no-site-file") ? nullptr : (root_loc / "etc/pharos.yaml").c_str(),
              vm.count("no-site-file") ? nullptr : "PHAROS_CONFIG",
              vm.count("no-user-file") ? nullptr : ".pharos.yaml"));
  if (vm.count("config")) {
    for (auto & filename : vm["config"].as<std::vector<bf::path>>()) {
      vm.config().mergeFile(filename.native());
    }
  }
  if (vm.count("option")) {
    for (auto & kv : vm["option"].as<std::vector<std::string>>()) {
      vm.config().mergeKeyValue(kv);
    }
  }
  if (vm.count("dump-config")) {
    // Use cout, so it can easily be copied to a file
    std::cout << vm.config() << LEND;
    exit(3);
  }

  global_logging_iteractive =
    global_logging_fileno >= 0 && isatty(global_logging_fileno) && !vm.count("batch");

  // Turn off color when color can't or shouldn't be used
  if (!color_terminal(global_logging_fileno) || vm.count("batch")) {
    get_logging_destination()->overridePropertiesNS().useColor = false;
  }

  get_logging_destination()->prefix()->showElapsedTime(vm["timing"].as<bool>());

  // Once the command line options have been processed we can determine the library path.
  auto lv = vm.get<bf::path>("library", "pharos.library");
  library_path = lv ? *lv : lib_root;

  // ----------------------------------------------------------------------------------------
  // Configure the rest of the logging facilities
  // ----------------------------------------------------------------------------------------

  Rose::Diagnostics::initialize();
  Sawyer::ProgressBarSettings::initialDelay(3.0);
  Sawyer::ProgressBarSettings::minimumUpdateInterval(1.0);

  // Add the options logging facility to the known facilities.
  Sawyer::Message::mfacilities.insert(olog);
  // We can now log to olog if we need to...

  if (vm.count("help")) {
    if (!proghelptext.empty()) {
      std::cout << proghelptext << "\n\n";
    }
    std::cout << partial_desc;
    std::cout << "\nRevID: " << pharos::REVISION << std::endl;
    exit(3);
  }

  if (vm.count("rose-version")) {
    std::cout << version_message() << std::endl;
    exit(3);
  }

  // We complain about non-included required options here.
  po::notify(vm);

  // Set global limits
  set_global_limits(vm);

  // Add the global logging facility to the known facilities.
  Sawyer::Message::mfacilities.insert(glog);
  // Initialize the semantics log with the appropriate filters.
  slog.initialize("FSEM");
  slog.initStreams(get_logging_destination());
  // Add the semantic logging facility to the known facilities.
  Sawyer::Message::mfacilities.insert(slog);
  // Initialize the prolog logging facility
  plog.initialize("PLOG");
  plog.initStreams(get_logging_destination());
  // Add the prolog logging facility to the known facilities.
  Sawyer::Message::mfacilities.insert(plog);

  // This was cleaner when it was a global initializer in types.cpp...
  init_type_logging();

  auto & apidblog = APIDictionary::initDiagnostics();
  BottomUpAnalyzer::initDiagnostics();

  // Set the default verbosity to only emit errors and and fatal messages.
  Sawyer::Message::mfacilities.control("none, >=error");
  // Since the line above doesn't seem to work as expected...
  slog[Sawyer::Message::WARN].disable();

  // We've recently decided to enable warnings as well.
  glog[Sawyer::Message::WARN].enable();
  // The options log is the exception to the rule that only errors and above are reported.
  olog[Sawyer::Message::WARN].enable();
  olog[Sawyer::Message::INFO].enable();

  if (interactive_logging()) {
    olog[Sawyer::Message::MARCH].enable();
  }

  // apidb
  apidblog[Sawyer::Message::WARN].enable();

  // Prolog logging levels
  plog[Sawyer::Message::ERROR].enable();
  plog[Sawyer::Message::FATAL].enable();
  plog[Sawyer::Message::WARN].enable();

  // If the user has modified the verbosity level, let's try to get the logging right before we
  // emit any messages at all.
  auto verbosity_opt = vm.get<int>("verbose", "pharos.verbosity");
  if (verbosity_opt) {
    int verbosity = *verbosity_opt;

    if (verbosity < 0) {
      OERROR << "Valid verbosity levels are 1-" << MAXIMUM_VERBOSITY << ", using 1." << LEND;
      verbosity = 1;
    }

    if (verbosity > MAXIMUM_VERBOSITY) {
      OERROR << "Valid verbosity levels are 1-" << MAXIMUM_VERBOSITY << ", using "
             << MAXIMUM_VERBOSITY << "." << LEND;
      verbosity = MAXIMUM_VERBOSITY;
    }

    // Enable warnings in the main program.
    glog[Sawyer::Message::WARN].enable(verbosity >= 1);
    // Enable informational messages from the main program.
    glog[Sawyer::Message::INFO].enable(verbosity >= 2);
    // Positively report options, including this one.
    olog[Sawyer::Message::WHERE].enable(verbosity >= 3);
    if (verbosity >= 3) {
      ODEBUG << "Verbose logging level set to " << verbosity << "." << LEND;
    }

    // Enable warnings from the semantics facility.
    slog[Sawyer::Message::WARN].enable(verbosity >= 4);
    // Enable (our) debugging from the main program.
    glog[Sawyer::Message::WHERE].enable(verbosity >= 5);
    // Enable informational messages from the semantics facility.
    slog[Sawyer::Message::INFO].enable(verbosity >= 6);
    // Enable more debugging from the main program.
    glog[Sawyer::Message::TRACE].enable(verbosity >= 7);
    // Enable everything from the main program.
    glog[Sawyer::Message::DEBUG].enable(verbosity >= 8);
    // Enable (our) debugging from the semantics facility.
    slog[Sawyer::Message::WHERE].enable(verbosity >= 9);
    // Enable more debugging in the options parsing code.
    olog[Sawyer::Message::TRACE].enable(verbosity >= 10);
    // Enable everything from the options parsing code.
    olog[Sawyer::Message::DEBUG].enable(verbosity >= 11);
    // Enable more debugging from the semantics facility.
    slog[Sawyer::Message::TRACE].enable(verbosity >= 12);
    // Enable everything from the semantics facility.
    slog[Sawyer::Message::DEBUG].enable(verbosity >= 13);

    // Prolog logging levels
    plog[Sawyer::Message::INFO].enable(verbosity >= 3);
    plog[Sawyer::Message::WHERE].enable(verbosity >= 6);
    plog[Sawyer::Message::TRACE].enable(verbosity >= 9);
    plog[Sawyer::Message::DEBUG].enable(verbosity >= 13);

    // Enable everything everywhere...
    if (verbosity >= MAXIMUM_VERBOSITY) {
      Sawyer::Message::mfacilities.control("all");
    }
  }

  // If the user chose to "override" the logging configuration, do that now instead.
  if (vm.count("log") > 0) {
    std::string cntrl = vm["log"].as<std::string>();
    // Now configure our logging.
    std::string error = Sawyer::Message::mfacilities.control(cntrl);
    if (error != "") {
      OERROR << "Control string error:" << error << LEND;
      // Rather than leave the logging in an unusual state, let's force a rational configuration.
      Sawyer::Message::mfacilities.control("none, >=error, OPTI(>=info)");
    }
  }

  // If we're actively debugging options parsing, test the logging infrastructure now.
  if (olog[TRACE]) {
    SWARN  << "Semantics WARN messages are enabled." << LEND;
    SINFO  << "Semantics INFO messages are enabled." << LEND;
    SDEBUG << "Semantics DEBUG/WHERE messages are enabled." << LEND;
    STRACE << "Semantics TRACE messages are enabled." << LEND;
    SCRAZY << "Semantics CRAZY/DEBUG messages are enabled." << LEND;
    GWARN  << "Main program WARN messages are enabled." << LEND;
    GINFO  << "Main program INFO messages are enabled." << LEND;
    GDEBUG << "Main program DEBUG/WHERE messages are enabled." << LEND;
    GTRACE << "Main program TRACE messages are enabled." << LEND;
    GCRAZY << "Main program CRAZY/DEBUG messages are enabled." << LEND;
  }

  if (fileopt) {
    if (vm.count("file")) {
      OINFO << "Analyzing executable: " << vm["file"].as<bf::path>().native() << LEND;
    } else {
      OFATAL << "You must specify at least one executable to analyze." << LEND;
      exit(3);
    }

    if (vm.count("include-func")) {
      for (const std::string & astr : vm["include-func"].as<StrVector>()) {
        rose_addr_t addr = parse_number(astr);
        ODEBUG << "Limiting analysis to function: " << addr_str(addr) << LEND;
      }
    }

    if (vm.count("exclude-func")) {
      for (const std::string & astr : vm["exclude-func"].as<StrVector>()) {
        rose_addr_t addr = parse_number(astr);
        ODEBUG << "Excluding function: " << addr_str(addr) << LEND;
      }
    }
  }

  bool propagate_conditions = (vm.count("propagate-conditions") > 0);
  if (propagate_conditions) {
    int limit = *vm.get<int>("maximum-nodes-per-condition", "pharos.maximum_nodes_per_condition");
    OINFO << "Propagating full conditions (up to " << limit
          << " terms) during function analysis." << LEND;
  }

  // Early checks to make sure APIDB files exist
  boost::optional<std::string> badfile = APIDictionary::verify_args(vm);
  if (badfile) {
    OFATAL << "Unable to read APIDB file: " << *badfile << LEND;
    exit (EXIT_FAILURE);
  }

  // We might want to investigate allowing unknown options for pasthru to ROSE
  // http://www.boost.org/doc/libs/1_53_0/doc/html/program_options/howto.html

  return vm;
}

ProgOptVarMap parse_cert_options(
  int argc, char** argv,
  ProgOptDesc od,
  const std::string & proghelptext,
  boost::optional<ProgPosOptDesc> posopt,
  Sawyer::Message::UnformattedSinkPtr destination) {
  try {
    return parse_cert_options_internal(argc, argv, od, proghelptext, posopt, destination);
  } catch (boost::program_options::error const &e) {
    OFATAL << "Error parsing arguments: " << e.what() << LEND;
    exit (EXIT_FAILURE);
  }
}

const bf::path& get_library_path()
{
  return library_path;
}

// Convert a vector of string into a set of rose_addr_t, out of option var map.
AddrSet option_addr_list(ProgOptVarMap const & vm, const char *name) {
  AddrSet aset;
  if (vm.count(name) > 0) {
    for (const std::string & as : vm[name].as<StrVector>()) {
      aset.insert(parse_number(as));
    }
  }
  return aset;
}

AddrSet get_selected_funcs(const DescriptorSet& ds, ProgOptVarMap const & vm) {
  AddrSet included_funcs = option_addr_list(vm, "include-func");

  // WTF?
  if (included_funcs.size () > 0) {
    for (rose_addr_t a : included_funcs) {
      ODEBUG << "Limiting analysis to function: " << addr_str(a) << LEND;
    }
  } else {
    auto & map = ds.get_func_map();
    std::transform(map.begin(), map.end(), std::inserter(included_funcs, included_funcs.end()),
                   [](FunctionDescriptorMap::const_reference x) { return x.first; });
    // There's a bug in boost::range::insert in boost 1.61 that causes failures in clang.
    // boost::insert (included_funcs, ds.get_func_map () | boost::adaptors::map_keys);
  }

  AddrSet excluded_funcs = option_addr_list(vm, "exclude-func");
  for (const rose_addr_t addr : excluded_funcs) {
    GINFO << "Function " << addr_str(addr) << " excluded" << LEND;
  }

  AddrSet selected_funcs;

  boost::set_difference (included_funcs, excluded_funcs, std::inserter (selected_funcs, selected_funcs.end ()));
  return selected_funcs;
}

void cleanup_our_globals() {
  // need to clean up some static globals that are or get things in them dynamically allocated,
  // to prevent static object ctor/dtor order problems w/ statically linked exec from crashing
  // in various ways at exit time during static object destruction:
  global_rops.reset(); // riscops.hpp
}

static void report_std_exception(const std::exception & e, int level = 0)
{
  if (level == 0) {
    GFATAL << "Pharos main error: ";
#ifdef __GNUC__
    int status;
    std::string ename (abi::__cxa_demangle(abi::__cxa_current_exception_type()->name(), 0, 0, &status));
    GFATAL << "(" << ename << ") ";
#endif
  }  else {
    GFATAL << std::string(level, ' ') << "Reason: ";
  }
  GFATAL << e.what() << LEND;
  try {
    std::rethrow_if_nested(e);
  } catch (const std::exception & e2) {
    report_std_exception(e2, level + 1);
  } catch (...) {
    // Do nothing
  }
}

static void report_exception(const std::exception_ptr ep)
{
  try {
    std::rethrow_exception (ep);
  } catch (const std::exception &e) {
    report_std_exception(e);
  } catch (...) {
#ifdef __GNUC__
    // totally non portable gcc specific stuff, courtesy of here:
    // http://stackoverflow.com/questions/4885334/c-finding-the-type-of-a-caught-default-exception/24997351#24997351
    std::string uxname(__cxa_current_exception_type()->name());
    int status = 0;
    char * buff = __cxxabiv1::__cxa_demangle(uxname.c_str(), NULL, NULL, &status);
    GFATAL << "Pharos main error, caught an unexpected exception named " << buff << LEND;
    std::free(buff);
#else
    GFATAL << "Pharos main error, caught an unexpected exception" << LEND;
#endif // __GNUC__
  }
}

static void pharos_terminate() {
  // Prevent loops
  std::set_terminate (std::terminate);

  // Report on the current exception
  report_exception (std::current_exception ());

  // Print the backtrace
  backtrace (glog, Sawyer::Message::FATAL);

  std::exit (EXIT_FAILURE);
}

int pharos_main(std::string const & glog_name, main_func_ptr fn,
                int argc, char **argv, int logging_fileno)
{
  glog.initialize(glog_name);

  int rc = 0;
  atexit(cleanup_our_globals);

  global_logging_fileno = logging_fileno;

  ROSE_INITIALIZE;

  if (!getenv(PHAROS_PASS_EXCEPTIONS_ENV))
    std::set_terminate (pharos_terminate);

  rc = fn(argc, argv);

  return rc;
}

} // namespace pharos

/* Local Variables:   */
/* mode: c++          */
/* fill-column:    95 */
/* comment-column: 0  */
/* End:               */

max_stars_repo_name: illbebach/pharos

max_issues_repo_name: illbebach/pharos

max_forks_repo_name: illbebach/pharos


text:
/*
 * simple_parser.hpp - Parser that only splits the command arguments and
 *                     supports quoted strings
 *
 *   Copyright 2010-2011 Jesús Torres <jmtorres@ull.es>
 *
 * 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.
 */

#ifndef SIMPLE_PARSER_HPP_
#define SIMPLE_PARSER_HPP_

#include <iostream>
#include <string>
#include <vector>

#include <boost/spirit/include/qi.hpp>

#include <cli/boost_parser_base.hpp>

namespace cli { namespace parser { namespace simpleparser
{
    namespace qi = boost::spirit::qi;
    namespace iso8859_1 = boost::spirit::iso8859_1;

    typedef std::vector<std::string> CommandArguments;

    //
    // Class SimpleParser
    //
    // The parser uses ISO-8859 encoding to avoid problems with UTF-8 strings
    // because the Boost.Spirit support of ASCII encoding launches an
    // exceptions when finds 8-bit characters.
    //

    struct SimpleParser
        : BoostParserBase<std::string, CommandArguments, iso8859_1::space_type>
    {
        SimpleParser();

        //
        // Parser rules
        //

        qi::rule<IteratorType> eol;
        qi::rule<IteratorType, char()> character;
        qi::rule<IteratorType, char()> escape;
        qi::rule<IteratorType, std::string(), iso8859_1::space_type> word;
        qi::rule<IteratorType, std::string(),
            iso8859_1::space_type> quotedString;
        qi::rule<IteratorType, std::string(),
            iso8859_1::space_type> doubleQuotedString;
        qi::rule<IteratorType, std::string(), iso8859_1::space_type> argument;
        qi::rule<IteratorType, SimpleParser::sig_type,
            iso8859_1::space_type> start;

        private:

            static void throwParserError(IteratorType const& first,
                IteratorType const& last, IteratorType const& error,
                const boost::spirit::info& info);
    };
}}}

namespace cli { namespace parser
{
    using simpleparser::SimpleParser;
}}

#endif /* SIMPLE_PARSER_HPP_ */

ext: hpp

max_stars_repo_name: JonathanTrEs/Shell

max_issues_repo_name: JonathanTrEs/Shell

max_forks_repo_name: JonathanTrEs/Shell


text:
//
// Created by Michael Linderman on 12/16/15.
//

#include <ostream>
#include <fstream>

#include <boost/filesystem.hpp>
#include <glog/logging.h>
#include <cppformat/format.h>
#include <htslib/hts.h>
#include <htslib/bgzf.h>
#include <htslib/tbx.h>

#include "aseq/util/exception.hpp"
#include "aseq/io/line.hpp"

using namespace aseq::util;
namespace fs = boost::filesystem;

namespace aseq {
namespace io {

namespace impl {

class ASCIIStreamLineWriter : public ASCIILineWriterInterface {
 public:
  ASCIIStreamLineWriter() = delete;

  explicit ASCIIStreamLineWriter(std::ostream &ostream) : ostream_(ostream) {}

  explicit ASCIIStreamLineWriter(const fs::path &path)
      : owned_ostream_(std::make_unique<std::ofstream>(path.native())), ostream_(*owned_ostream_) {}

  virtual void Write(const Line &line) { ostream_ << line; }

 private:
  std::unique_ptr<std::ostream> owned_ostream_;
  std::ostream &ostream_;
};

class BGZipLineWriter : public ASCIILineWriterInterface {
 public:
  BGZipLineWriter() = delete;

  BGZipLineWriter(const fs::path &path, FileFormat format)
      : file_(nullptr, &hts_close), format_(format) {
    file_.reset(hts_open(path.c_str(), "wz"));
    if (!file_) {
      throw file_write_error() << error_message("could not open tabix file for writing");
    }
  }
  ~BGZipLineWriter() {
    tbx_conf_t const *conf = nullptr;
    switch (format_) {
      default:
        break;
      case FileFormat::VCF4_1:
      case FileFormat::VCF4_2:
        conf = &tbx_conf_vcf;
    }
    if (conf) {
      fs::path path(file_->fn);
      file_.reset(nullptr);  // Force closure of file
      if (!fs::exists(path) || tbx_index_build(path.c_str(), 0, conf) != 0) {
        throw file_write_error() << error_message("could not generate tabix index");
      }
    }
  }

  virtual void Write(const Line &line) {
    bgzf_write(file_->fp.bgzf, line.begin(), line.end() - line.begin());
  }

 private:
  std::unique_ptr<htsFile, decltype(&hts_close)> file_;
  FileFormat format_;
};

}  // namespace impl

ASCIILineWriterInterface::FactoryResult ASCIILineWriterInterface::MakeLineWriter(
    std::ostream &ostream) {
  return FactoryResult(new impl::ASCIIStreamLineWriter(ostream));
}

ASCIILineWriterInterface::FactoryResult ASCIILineWriterInterface::MakeLineWriter(
    const fs::path &path, FileFormat format) {
  LOG_IF(INFO, fs::exists(path)) << fmt::format("Overwriting existing file {}", path.native());

  if (path.extension() == ".gz")
    return std::make_unique<impl::BGZipLineWriter>(path, format);
  else
    return std::make_unique<impl::ASCIIStreamLineWriter>(path);
}
}  // namespace io
}  // namespace aseq

ext: cpp

max_stars_repo_name: mlinderm/aseq

max_issues_repo_name: mlinderm/aseq

max_forks_repo_name: mlinderm/aseq


text:
// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef MTL_VECTOR_MAP_VIEW_INCLUDE
#define MTL_VECTOR_MAP_VIEW_INCLUDE

#include <utility>
#include <boost/shared_ptr.hpp>
#include <boost/numeric/mtl/concept/collection.hpp>
#include <boost/numeric/mtl/utility/category.hpp>
#include <boost/numeric/mtl/utility/range_generator.hpp>
#include <boost/numeric/mtl/utility/property_map.hpp>
#include <boost/numeric/mtl/utility/copy_expression_const_ref_container.hpp>
#include <boost/numeric/mtl/operation/sfunctor.hpp>
#include <boost/numeric/mtl/operation/tfunctor.hpp>
#include <boost/numeric/mtl/operation/conj.hpp>
#include <boost/numeric/mtl/operation/real.hpp>
#include <boost/numeric/mtl/operation/imag.hpp>
#include <boost/numeric/mtl/vector/vec_expr.hpp>

namespace mtl { namespace vector { namespace detail {
    // Forward declaration for friend declaration
    template <typename, typename> struct map_value;
}}}

namespace mtl { namespace vector {

template <typename Functor, typename Vector>
struct map_view
  : public vec_expr< map_view<Functor, Vector> >
{
    typedef map_view                                   self;
    typedef vec_expr< self >                           expr_base;
    typedef Vector                                     other;

    typedef typename Functor::result_type              value_type;
    typedef typename Functor::result_type              const_reference;
    typedef typename Collection<Vector>::size_type     size_type;

    map_view (const Functor& functor, const other& ref)
      : expr_base(*this), functor(functor), ref(ref)
    {
	ref.delay_assign();
    }

    map_view (const Functor& functor, boost::shared_ptr<Vector> p)
      : expr_base(*this), functor(functor), my_copy(p), ref(*p)
    {
	ref.delay_assign();
    }

#ifdef MTL_WITH_CPP11_MOVE
  map_view (self&& that) : functor(that.functor), my_copy(std::move(that.my_copy)), ref(that.ref) {}
  map_view (const self& that) : functor(that.functor), ref(that.ref) { assert(that.my_copy.use_count() == 0); }
#endif

    friend size_type inline num_rows(const self& v) { return num_rows(v.ref); }
    friend size_type inline num_cols(const self& v) { return num_cols(v.ref); }

    size_type stride() const { 	return ref.stride(); }
    const_reference operator() (size_type i) const { return functor(ref(i)); }
    const_reference operator[] (size_type i) const { return functor(ref[i]); }
    void delay_assign() const {}

    template <typename, typename> friend struct detail::map_value;

  protected:
    boost::shared_ptr<Vector>           my_copy;
  public:
    Functor           functor;
    // ref is a const& if Vector is a true vector and a copy if it is an expression
    typename mtl::traits::copy_expression_const_ref_container<Vector>::type ref;
};

template <typename Functor, typename Vector>
inline std::size_t size(const map_view<Functor, Vector>& v)
{     return mtl::vector::size(v.ref); }

// ================
// Free functions
// ================

    namespace detail {

	template <typename Functor, typename Vector>
	struct map_value
	{
	    typedef typename Vector::key_type                      key_type;
	    typedef typename vector::map_view<Functor, Vector>::value_type value_type;

	    map_value(vector::map_view<Functor, Vector> const& map_vector)
		: map_vector(map_vector), its_value(map_vector.ref)
	    {}

	    value_type operator() (key_type const& key) const
	    {
		return map_vector.functor(its_value(key));
	    }

	  protected:
	    vector::map_view<Functor, Vector> const&   map_vector;
	    typename ::mtl::traits::const_value<Vector>::type its_value;
        };

    } // detail

}} // namespace mtl::vector

namespace mtl { namespace traits {

    // ================
    // Property maps
    // ================

    template <typename Functor, typename Vector>
    struct index<vector::map_view<Functor, Vector> >
	: public index<Vector>
    {};

    template <typename Functor, typename Vector>
    struct const_value<vector::map_view<Functor, Vector> >
    {
	typedef vector::detail::map_value<Functor, Vector>  type;
    };

    // ================
    // Range generators
    // ================

    // Use range_generator of original vector
    template <typename Tag, typename Functor, typename Vector>
    struct range_generator<Tag, vector::map_view<Functor, Vector> >
	: public range_generator<Tag, Vector>
    {};

}} // mtl::traits

namespace mtl { namespace vector {

template <typename Scaling, typename Vector>
struct scaled_view
    : public map_view<tfunctor::scale<Scaling, typename Vector::value_type>, Vector>
{
    typedef tfunctor::scale<Scaling, typename Vector::value_type>  functor_type;
    typedef map_view<functor_type, Vector>                         base;
    typedef scaled_view                                            self;

    explicit scaled_view(const Scaling& scaling, const Vector& vector)
      : base(functor_type(scaling), vector)
    {}

    explicit scaled_view(const Scaling& scaling, boost::shared_ptr<Vector> p)
      : base(functor_type(scaling), p)
    {}

#ifdef MTL_WITH_CPP11_MOVE
    scaled_view (self&& that) : base(that) {}
    scaled_view (const self& that) : base(that) {}
#endif
};

// added by Hui Li
template <typename Vector, typename RScaling>
struct rscaled_view
  : public map_view<tfunctor::rscale<typename Vector::value_type, RScaling>, Vector>
{
    typedef tfunctor::rscale<typename Vector::value_type, RScaling>  functor_type;
    typedef map_view<functor_type, Vector>                          base;
    typedef rscaled_view                                            self;

    explicit rscaled_view(const Vector& vector, const RScaling& rscaling)
      : base(functor_type(rscaling), vector)
    {}

    explicit rscaled_view(boost::shared_ptr<Vector> p, const RScaling& rscaling)
      : base(functor_type(rscaling), p)
    {}

#ifdef MTL_WITH_CPP11_MOVE
    rscaled_view (self&& that) : base(that) {}
    rscaled_view (const self& that) : base(that) {}
#endif
};

// added by Hui Li
template <typename Vector, typename Divisor>
struct divide_by_view
  : public map_view<tfunctor::divide_by<typename Vector::value_type, Divisor>, Vector>
{
    typedef tfunctor::divide_by<typename Vector::value_type, Divisor>  functor_type;
    typedef map_view<functor_type, Vector>                             base;
    typedef divide_by_view                                             self;

    explicit divide_by_view(const Vector& vector, const Divisor& div)
      : base(functor_type(div), vector)
    {}

    explicit divide_by_view(boost::shared_ptr<Vector> p, const Divisor& div)
      : base(functor_type(div), p)
    {}

#ifdef MTL_WITH_CPP11_MOVE
    divide_by_view (self&& that) : base(that) {}
    divide_by_view (const self& that) : base(that) {}
#endif
};

template <typename Vector>
struct conj_view
  : public map_view<mtl::sfunctor::conj<typename Vector::value_type>, Vector>
{
    typedef mtl::sfunctor::conj<typename Vector::value_type>            functor_type;
    typedef map_view<functor_type, Vector>                         base;
    typedef conj_view                                                   self;

    explicit conj_view(const Vector& vector)
      : base(functor_type(), vector)
    {}

    explicit conj_view(boost::shared_ptr<Vector> p)
      : base(functor_type(), p)
    {}

#ifdef MTL_WITH_CPP11_MOVE
    conj_view (self&& that) : base(that) {}
    conj_view (const self& that) : base(that) {}
#endif
};

template <typename Vector>
struct real_view
  : public map_view<mtl::sfunctor::real<typename Vector::value_type>, Vector>
{
    typedef mtl::sfunctor::real<typename Vector::value_type>            functor_type;
    typedef map_view<functor_type, Vector>                         base;
  typedef real_view                                              self;

    explicit real_view(const Vector& vector)
      : base(functor_type(), vector)
    {}

    explicit real_view(boost::shared_ptr<Vector> p)
      : base(functor_type(), p)
    {}

#ifdef MTL_WITH_CPP11_MOVE
    real_view (self&& that) : base(that) {}
    real_view (const self& that) : base(that) {}
#endif
};

template <typename Vector>
struct imag_view
  : public map_view<mtl::sfunctor::imag<typename Vector::value_type>, Vector>
{
    typedef mtl::sfunctor::imag<typename Vector::value_type>            functor_type;
    typedef map_view<functor_type, Vector>                         base;
    typedef imag_view                                              self;

    explicit imag_view(const Vector& vector)
      : base(functor_type(), vector)
    {}

    explicit imag_view(boost::shared_ptr<Vector> p)
      : base(functor_type(), p)
    {}

#ifdef MTL_WITH_CPP11_MOVE
    imag_view (self&& that) : base(that) {}
    imag_view (const self& that) : base(that) {}
#endif
};

template <typename Vector>
struct negate_view
  : public map_view<mtl::sfunctor::negate<typename Vector::value_type>, Vector>
{
    typedef mtl::sfunctor::negate<typename Vector::value_type>            functor_type;
    typedef map_view<functor_type, Vector>                                base;
    typedef negate_view                                                   self;

    explicit negate_view(const Vector& vector)
      : base(functor_type(), vector)
    {}

    explicit negate_view(boost::shared_ptr<Vector> p)
      : base(functor_type(), p)
    {}

#ifdef MTL_WITH_CPP11_MOVE
    negate_view (self&& that) : base(that) {}
    negate_view (const self& that) : base(that) {}
#endif
};

}} // namespace mtl::vector

namespace mtl { namespace sfunctor {

    template <typename Vector>
    struct conj_aux<Vector, tag::vector>
    {
	typedef mtl::vector::conj_view<Vector> result_type;

	static inline result_type apply(const Vector& vector)
	{
	    return result_type(vector);
	}

	result_type operator() (const Vector& vector) const
	{
	    return apply(vector);
	}
    };

}}

#endif // MTL_VECTOR_MAP_VIEW_INCLUDE


text:
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Mutex.h>
#include <muduo/base/Thread.h>
#include <muduo/base/Timestamp.h>

#include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <vector>
#include <stdio.h>

using namespace muduo;
using namespace std;

MutexLock g_mutex;
vector<int> g_vec;
const int kCount = 10*1000*1000;

void threadFunc()
{
  for (int i = 0; i < kCount; ++i)
  {
    MutexLockGuard lock(g_mutex);
    g_vec.push_back(i);
  }
}

int foo() __attribute__ ((noinline));

int g_count = 0;
int foo()
{
  MutexLockGuard lock(g_mutex);
  if (!g_mutex.isLockedByThisThread())
  {
    printf("FAIL\n");
    return -1;
  }

  ++g_count;
  return 0;
}

int main()
{
  MCHECK(foo());
  if (g_count != 1)
  {
    printf("MCHECK calls twice.\n");
    abort();
  }

  const int kMaxThreads = 8;
  g_vec.reserve(kMaxThreads * kCount);

  Timestamp start(Timestamp::now());
  for (int i = 0; i < kCount; ++i)
  {
    g_vec.push_back(i);
  }

  printf("single thread without lock %f\n", timeDifference(Timestamp::now(), start));

  start = Timestamp::now();
  threadFunc();
  printf("single thread with lock %f\n", timeDifference(Timestamp::now(), start));

  for (int nthreads = 1; nthreads < kMaxThreads; ++nthreads)
  {
    boost::ptr_vector<Thread> threads;
    g_vec.clear();
    start = Timestamp::now();
    for (int i = 0; i < nthreads; ++i)
    {
      threads.push_back(new Thread(&threadFunc));
      threads.back().start();
    }
    for (int i = 0; i < nthreads; ++i)
    {
      threads[i].join();
    }
    printf("%d thread(s) with lock %f\n", nthreads, timeDifference(Timestamp::now(), start));
  }
}

ext: cc

max_stars_repo_name: NPS-TECH/muduo

max_issues_repo_name: NPS-TECH/muduo

max_forks_repo_name: NPS-TECH/muduo


text:
/*
// Copyright (c) 2000-2009, Texas Engineering Experiment Station (TEES), a
// component of the Texas A&M University System.

// All rights reserved.

// The information and source code contained herein is the exclusive
// property of TEES and may not be disclosed, examined or reproduced
// in whole or in part without explicit written authorization from TEES.
*/

#ifndef STAPL_PARAGRAPH_EDGE_ENTRY_HPP
#define STAPL_PARAGRAPH_EDGE_ENTRY_HPP

#include <boost/intrusive/list.hpp>
#include <boost/intrusive/unordered_set.hpp>

#include <stapl/paragraph/edge_container/edge_entry_base.hpp>
#include <stapl/paragraph/edge_container/edge_notifier_wrapper.hpp>
#include <stapl/paragraph/edge_container/edge_remote_notifier.hpp>

#include <stapl/paragraph/edge_container/remote_edge_version_entry.hpp>

#include <stapl/utility/down_cast.hpp>
#include <stapl/utility/empty_class.hpp>

#include <ostream>

namespace stapl {

namespace detail {

//////////////////////////////////////////////////////////////////////
/// @brief Wraps notifier passed to edge_entry.  Provides storage and access
/// method for the location associated with the base remote notifier.
///
/// @ingroup pgEdgeEntry
///
/// @tparam Notifier The base remote notifier to be used.
//////////////////////////////////////////////////////////////////////
template<typename Notifier>
class remote_notifier
  : private Notifier,       // inheritance for empty base optimization.
    public location_storage // location this remote notifier refers to.
{
public:
  remote_notifier(Notifier notifier, size_t loc)
    : Notifier(std::move(notifier)),
      location_storage(loc)
  { }

  STAPL_USE_MANAGED_ALLOC(remote_notifier)

  //////////////////////////////////////////////////////////////////////
  /// @brief Wraps function operator of @p Notifier, passing the
  ///        associated location appropriately in the parameter list.
  ///
  /// @param ct The @p edge_container of the PARAGRAPH.
  ///
  /// @param tid Task identifier of the producer task for this edge.
  ///
  /// @param value Produced value to be flowed to the remote consumer location.
  ///
  /// @param b_migrate Whether this invocation represents notification or
  ///                  or notifier migration request.
  ///
  /// @sa edge_notifier_wrapper
  //////////////////////////////////////////////////////////////////////
  template<typename T>
  void operator()(edge_container& ct, std::size_t tid,
                  edge_version_storage<T> const& version,
                  bool b_migrate, bool b_moveable) const
  {
    Notifier::operator()(
      ct, this->location(), tid, version, b_migrate, b_moveable);
  }
}; // class remote_notifier

//////////////////////////////////////////////////////////////////////
/// @brief Lazily provides access to the @ref edge_version_storage
/// associated with an edge version entry if desired by another
/// version entry.
///
/// Used to allow access to the full version value by remote version
/// entry list if needed to satisfy notifications.
//////////////////////////////////////////////////////////////////////
template<typename T>
class entry_storage_access
{
private:
  using entry_t =
    edge_version_entry<
      T, boost::function<void (executor_base&, T const&)>,
      boost::intrusive::make_slist_base_hook<>::type>;

  entry_t const* m_entry_ptr;

public:
  entry_storage_access(entry_t const* entry_ptr)
    : m_entry_ptr(entry_ptr)
  { }

  edge_version_storage<T> const&
  operator()(void) const
  {
    stapl_assert(m_entry_ptr != nullptr,
                 "attempting to deferefence null entry pointer");

    return m_entry_ptr->version_storage();
  }
}; // class entry_storage_access

//////////////////////////////////////////////////////////////////////
/// @brief Lazily provides access to the value in an edge version
/// entry's underlying storage if desired by another version entry.
///
/// Used to allow access to the full version value by filtered version
/// entry list if needed to satisfy notifications.
//////////////////////////////////////////////////////////////////////
template<typename T>
class entry_value_access
{
private:
  using entry_t =
    edge_version_entry<
      T, boost::function<void (executor_base&, T const&)>,
      boost::intrusive::make_slist_base_hook<>::type>;

  entry_t const* m_entry_ptr;

public:
  entry_value_access(entry_t const* entry_ptr)
    : m_entry_ptr(entry_ptr)
  { }

  T const& operator()(void) const
  {
    stapl_assert(m_entry_ptr != nullptr,
                 "attempting to deferefence null entry pointer");

    return m_entry_ptr->value();
  }
}; // class entry_value_access

//////////////////////////////////////////////////////////////////////
/// @brief The primary storage class for dependence and data flow values
/// in the PARAGRAPH.  One instance of this class or the base class
/// @ref edge_entry_base exists for each producer task, both on the
/// location where it executes as well as on all locations with active
/// consumers.
///
/// @tparam The return type of the producer task's workfunction.
///
/// @ingroup pgEdgeEntry
///
/// Each entry maintains a collection of notifier lists called versions.
/// A version may be held in the entry object frame (for now remote and signal
/// versions are held this way) or in a versions list held as data member
/// (currently the case for filtered and full versions).  This is strictly an
/// implementation detail that likely warrants further investigation.
///
/// The only component interacting with the edge_entry directly is the
/// @p edge_container which maintains all entries for a given location in a
/// container.  The @p edge_container redirects most requests from outside
/// callers to the appropriate entry methods.
///
/// @todo Boost.Function usage, while generic for notifier storage, may
/// introduce overhead.  Verify the genericity is actually needed or if the
/// notifier types are generally well known and can be used directly with a
/// little code refactoring.
///
/// @todo A smarter, adaptive storage methodology for versions that decided
/// based on needs of the PARAGRAPH (i.e., does it needs signal, filters, etc)
/// whether to store a version in the edge_entry frame or in a list such as
/// m_version could reduce memory usage and increase locality for the common,
/// simple uses of this class template.  For example, edge_entry<T, FULL_ONLY>.
///
/// @todo Consider another derived class of edge_entry_base, specifically for
/// use on the producer location.  Doing so would reduce the memory consumption
/// on consumer only locations (at the expense of additional code and template
/// instantiations.
///
/// @todo The lifetime of all value versions are tied directly to the full
/// entry.  Keeping per version reference counts could allow us to retire
/// individual versions as they are no longer need and (maybe) reduce overall
/// @p edge_container storage.
///
/// @todo Implementing an apply_if_exists(req, F) for the versions list could
/// avoid some unnecessary creations of versions that don't actually need to
/// exist (at least exist yet).
///
/// @todo The lookup of the full entry some contexts (i.e.,
/// @p add_remote_notifier) could be avoided by caching the full flow_set bit
/// in the entry frame and pass a functor other versions to retrieve the full
/// value only if needed.
//////////////////////////////////////////////////////////////////////
template<typename T>
class edge_entry
  : public edge_entry_base
{
private:
  /// @brief the actual stored data type in the entry.
  typedef typename df_stored_type<T>::type          stored_value_t;

  /// @brief The base class shared by full and filtered versions which defines
  /// an entry in the versions list.
  typedef edge_version_entry_base<
    boost::intrusive::make_slist_base_hook<>::type> edge_version_entry_t;

  /// @brief The versions list type which holds full and filtered version
  /// notifier lists.
  typedef boost::intrusive::make_slist<
    edge_version_entry_t,
    boost::intrusive::constant_time_size<false>,
    boost::intrusive::cache_last<true>>::type       edge_version_list_t;

  /// @brief The type of the full version notifier list.
  typedef edge_version_entry<
    stored_value_t,
    boost::function<
      void (executor_base&, stored_value_t const&)>,
    boost::intrusive::make_slist_base_hook<>::type> full_edge_version_entry_t;

  /// @brief The type of the filtered versions notifier list.
  typedef filtered_edge_versions<
    stored_value_t,
    boost::intrusive::make_slist_base_hook<>::type> filtered_edge_versions_t;

  /// @brief Initial wrapper placed around remote notifier to erase concrete
  /// type.
  typedef boost::function<
    void (edge_container&, size_t, std::size_t,
          edge_version_storage<stored_value_t> const&, bool, bool)
  >                                                 base_remote_notifier_t;

  /// @brief The remote notifier stored by this edge_entry.  Wraps all the
  /// @p base_remote_notifier_t and stores the associated remote location.
  typedef remote_notifier<base_remote_notifier_t>   remote_notifier_t;

  /// @brief The remote version list type.
  typedef remote_edge_version_entry<
    stored_value_t, remote_notifier_t, empty_class> remote_edge_version_entry_t;

public:
  /// @brief Remote version entry type reflected publicly for migration support.
  typedef typename remote_edge_version_entry_t::entry_t remote_notifier_entry_t;

/// @brief Remote version list type reflected publicly for migration support.
  typedef typename remote_edge_version_entry_t::notifier_list_t
    remote_notifier_list_t;

private:
  typedef typename edge_version_list_t::const_iterator  versions_citer_t;
  typedef typename edge_version_list_t::iterator        versions_iter_t;

  /// @brief List of notifier lists associated with different versions of the
  /// produced value.  Currently contains at most one full entry and one
  /// filtered entry (which stores a list of all filtered versions).
  edge_version_list_t                                   m_versions;

  /// @brief Stores notifiers for remote consumer locations.
  remote_edge_version_entry_t                           m_remote_version;

public:
  edge_entry& operator=(edge_entry const&) = delete;
  edge_entry(edge_entry const&)            = delete;

  explicit
  edge_entry(std::size_t tid, bool b_persistent)
    : edge_entry_base(tid, b_persistent, false),
      m_remote_version()
  { }

  //////////////////////////////////////////////////////////////////////
  /// @brief Constructor called to upgrade a edge_entry_base element of the
  /// edge_container to edge value typed instance of this class template.
  ///
  /// This is effectively a move constructor and that calls the similarly
  /// defined move constructor of @ref edge_entry_base.
  //////////////////////////////////////////////////////////////////////
  explicit
  edge_entry(edge_entry_base&& other)
    : edge_entry_base(std::move(other))
  { }

  //////////////////////////////////////////////////////////////////////
  /// @brief Remove all notifications if not part of persistent PARAGRAPH
  /// and remove version entries stored @p m_versions.
  //////////////////////////////////////////////////////////////////////
  ~edge_entry() final
  {
    if (!this->is_persistent())
      this->cleanup_notifications();

    m_versions.clear_and_dispose(version_entry_disposer());

    stapl_assert(this->persistent_evictable(),
      "~edge_entry(): not evictable");
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Clear all local and remote notifications stored in the entry.
  ///
  /// Either called in the destructor or as part of the delayed edge_container
  /// destruction used when persistency support is enabled.
  //////////////////////////////////////////////////////////////////////
  void cleanup_notifications(void) final
  {
    this->m_signal_version.cleanup_notifications();
    this->m_remote_version.cleanup_notifications();

    for (auto&& version : m_versions)
      version.cleanup_notifications();
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Freestanding version of operator defined for the @p edge_entry
  /// in terms of the entries' task identifiers.  Used by the Boost.Intrusive
  /// @p unordered_set in which all entries are stored by the @p edge_container.
  //////////////////////////////////////////////////////////////////////
  friend bool operator==(edge_entry const& lhs, edge_entry const& rhs)
  {
    return lhs.m_tid == rhs.m_tid;
  }

  STAPL_USE_MANAGED_ALLOC(edge_entry)

  //////////////////////////////////////////////////////////////////////
  /// @brief Define insertion operator on ostream. Provides useful debugging
  /// information.
  ///
  /// @param os The output stream to insert into.
  /// @param entry The edge entry to insert information about into the stream.
  //////////////////////////////////////////////////////////////////////
  friend
  std::ostream& operator<<(std::ostream& os, edge_entry<T> const& entry)
  {
    return os << "Cache Entry:\n"
      << "Tid                        = " << entry.m_tid << "\n"
      << "Unknown Consumer Cnt       = " << entry.m_unknown_consumer_cnt << "\n"
      << "Local User Cnt             = " << entry.m_local_user_cnt << "\n"
      << "Empty Local Notifications  = " <<
      entry.empty_local_notifications() << "\n"
      << "Empty Remote Notifications = "
      << entry.m_remote_version.empty_notifications() << "\n"
      << "Flushing Remote            = "
      << entry.m_remote_version.test_flushing() << "\n";
  }

private:
  //////////////////////////////////////////////////////////////////////
  /// @brief Return true if all signal, filter, and full consumer notification
  /// lists are empty.
  ///
  /// @sa edge_container::setup_flow(
  //////////////////////////////////////////////////////////////////////
  bool empty_local_notifications() const
  {
    if (!this->empty_signal_notifications())
      return false;

    for (auto&& version : m_versions)
      if (!version.empty_notifications())
        return false;

    return true;
  }

public:
  //////////////////////////////////////////////////////////////////////
  /// @brief Return true if there are any local notifications in this entry
  /// that have not yet been flushed by a data flow trigger.
  //////////////////////////////////////////////////////////////////////
  bool has_unnotified_local_consumer() const
  {
    return !this->empty_local_notifications();
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Receive a local consumption request with associated @p notifier
  ///   and @p filter.  Integrate it into edge_entry's local dependence
  ///   representation and signal caller (@ref edge_container::setup_flow)
  ///   if any remote notification is required.
  ///
  /// @param ct the @p edge_container holding this @p edge_entry.
  /// @param notifier The notifier to invoke when the requested data flow
  ///   is complete and available for consumption.
  /// @param filter The filtering function to be applied on the producer task's
  ///   value prior to passing it successor.
  /// @param b_inc_local_usr_cnt Denotes whether this is a consumer that will
  ///   rely on the entry's copy of the flowed value.  Guides internal reference
  ///   counting.
  /// @return Pair containing add_status variable to guide creation of
  ///  remote notifier and the version identifier associated with filter
  ///  to use if remote notifier is created.
  //////////////////////////////////////////////////////////////////////
  template<typename Filter>
  tuple<df_add_status, unsigned int,
        edge_version_storage<
          typename df_stored_type<
            typename boost::result_of<
              Filter(stored_value_t)>::type>::type>&>
  add_local_notifier(edge_container& ct,
                     boost::function<
                       void (executor_base&,
                             typename df_stored_type<
                               typename boost::result_of<
                                 Filter(typename df_stored_type<T>::type const&)
                               >::type
                             >::type const&)
                     > const& notifier,
                     bool b_inc_local_usr_cnt,
                     Filter const& filter)
  {
    // If filtered or full data is flowed (this isn't just a signal), a proxy
    // to this value will be constructed ((i.e., not copy constructed) either
    // in create_task() or by the return value of the task graph.  Increment the
    // local_user_cnt to hold value (even after event notifier invoked) until
    // this occurs.  Note the destructor of the edge_accessor contains the
    // matching decrement.
    if (b_inc_local_usr_cnt)
      this->increment_local_consumers();

    filtered_edge_versions_t& filtered_versions = get_filtered_versions();

    auto full_iter = this->iterator_lookup_version(FULL);

    full_edge_version_entry_t* const full_ptr =
      full_iter == m_versions.end()
      ?  nullptr : &down_cast<full_edge_version_entry_t&>(*full_iter);

    auto ret_val =
      filtered_versions.add_notifier(
        notifier, filter, ct.tg().executor(),
        entry_value_access<stored_value_t>(full_ptr),
        full_ptr == nullptr ? false : full_ptr->test_flow(),
        ct.tg().migration_enabled(), is_persistent());

    typedef tuple<
      df_add_status, unsigned int,
      edge_version_storage<
        typename df_stored_type<
          typename boost::result_of<
            Filter(stored_value_t)>::type>::type>&> return_t;

    return return_t(
      this->compute_request_status(FILTERED, get<0>(ret_val), get<2>(ret_val)),
      get<1>(ret_val) + 1,
      get<3>(ret_val));
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Signature of function for identity / full value consumption.
  //////////////////////////////////////////////////////////////////////
  tuple<df_add_status, unsigned int,
        edge_version_storage<stored_value_t>&>
  add_local_notifier(edge_container& ct,
                     boost::function<
                       void (executor_base&, stored_value_t const&)
                     > const& notifier,
                     bool b_inc_local_usr_cnt,
                     detail::df_identity<stored_value_t> const&)
  {
    // If filtered or full data is flowed (this isn't just a signal), a proxy
    // to this value will be constructed ((i.e., not copy constructed) either
    // in create_task() or by the return value of the task graph.  Increment the
    // local_user_cnt to hold value (even after event notifier invoked) until
    // this occurs.  Note the destructor of the edge_accessor contains the
    // matching decrement.
    if (b_inc_local_usr_cnt)
      this->increment_local_consumers();

    full_edge_version_entry_t& full_version = get_full_version();

    auto& storage_ref =
      full_version.add_notifier(notifier, ct.tg().executor(), is_persistent());

    return tuple<df_add_status, unsigned int,
                 edge_version_storage<stored_value_t>&>
      (this->compute_request_status(FULL, full_version.test_flow()),
       0, storage_ref);
  }

  edge_version_storage<stored_value_t>&
  get_full_version_storage(void)
  {
    return get_full_version();
  }

  edge_version_storage<stored_value_t> const&
  get_full_version_storage(void) const
  {
    return get_full_version();
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Add a notifier to transmit a producer value (full or filtered) to
  /// a remote consumer location and trigger data flow of local consumer
  /// edges there.
  ///
  /// @param ct The edge container this entry is held by.
  /// @param notifier The notifier to called when the producer task finishes
  ///   execution (encapsulate any filter function).
  /// @param loc The location this notifier originated from.
  /// @param b_upgrade Denotes whether this request upgrades a previous remote
  ///   notification from location @p loc to higher request level
  ///   (i.e., FILTERED --> FULL).
  ///
  /// @sa remote_edge_version_entry
  ///
  /// Currently only called on the producer location.
  /// @todo Use is_moveable<T> metafunction from runtime to guard generation of
  ///   move code paths for basic types, etc.
  //////////////////////////////////////////////////////////////////////
  template<typename Notifier>
  void
  add_remote_notifier(edge_container& ct,
                      Notifier const& notifier,
                      const size_t loc,
                      const bool b_upgrade)
  {
    stapl_assert(this->local_producer_initialized(),
      "edge_entry::add_remote_notifier: notifier for uninitialized producer");

    full_edge_version_entry_t const& full_version = get_full_version();

    const bool b_moveable = m_unknown_consumer_cnt==1 && m_local_user_cnt==0;

    m_remote_version.add_notifier(
      remote_notifier_t(notifier, loc), tid(), ct,
      entry_storage_access<stored_value_t>(&full_version),
      full_version.test_flow(),
      is_persistent(), b_upgrade, b_moveable
#ifndef STAPL_NDEBUG
      , m_b_consumers_before_producer
#endif
    );

    // Regardless of whether we can send the value yet or not, we've been
    // notified of a previously unknown consumer.  Decrement unknown count.
    this->decrement_unknown_consumers();
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Add a signal notifier to notify a remote consumer location
  /// when a producer task finishes execution.
  ///
  /// @param ct The edge container this entry is held by.
  /// @param loc The location this notifier originated from.
  //////////////////////////////////////////////////////////////////////
  void add_remote_signal_notifier(edge_container& ct, size_t loc) final
  {
    typedef edge_notifier_wrapper<
      T, detail::edge_remote_signal_notifier<stored_value_t>> notifier_t;

    this->add_remote_notifier(ct, notifier_t(), loc, false);
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Used to trigger data flow in this entry for edges that can be
  /// serviced with the version of the value that has been passed via
  /// immutable sharing.
  //////////////////////////////////////////////////////////////////////
  void set_shared_value(immutable_shared<stored_value_t> const& wrapper,
                        edge_container& ct, unsigned int version_id,
                        bool b_caller_is_set_element = false,
                        bool b_caller_is_migration   = false)
  {
    stapl_assert(!b_caller_is_migration, "immutable detected, migration");
    stapl_assert(version_id == 0, "immutable, filtering");

    const bool b_migration_enabled = ct.tg().migration_enabled();

    // Using a pointer to handle case when edge_container backs a
    // pg input port initialized prior to executor construction.
    // notifications can't exist (as factory hasn't been called for setup
    // of internal consumers), but value may be set if flow already available.
    auto executor_ptr = ct.tg().executor_ptr();

    // Either filtered or full means signal requests can be notified.
    if (!this->signal_set())
    {
      // Passing a pointer to handle case when edge_container backs a
      // PG input port initialized prior to executor construction.
      // Notifications can't exist (as factory hasn't been called for setup
      // of internal consumers.
      this->set_signal(executor_ptr, b_migration_enabled);
    }
    else
      stapl_assert(this->is_persistent() || this->empty_signal_notifications(),
        "m_b_signal_flow_set with pending SIGNAL notifications");

    constexpr bool b_expect_exist = false;

    auto& full_version_entry = get_full_version(b_expect_exist);

    full_version_entry.set_value(
      wrapper, executor_ptr, b_migration_enabled, ct.is_persistent());

    versions_iter_t filtered_iter = iterator_lookup_version(FILTERED);

    // val is subject to move above and may be emptied now. Use value stored
    // in full_version_entry to service requests below (which are initialized
    // by copy, for now).
    if (b_caller_is_set_element)
    {
      constexpr bool b_moveable = false;

      stapl_assert(!b_moveable || filtered_iter == m_versions.end(),
        "expected empty filtered notifier list");

      m_remote_version.set_value(
        ct, tid(), entry_storage_access<stored_value_t>(&full_version_entry),
        is_persistent(), b_moveable);
    }

    // use set_value_full so filtered_version applies filter before notifying.
    if (filtered_iter != m_versions.end())
    {
      down_cast<filtered_edge_versions_t&>(*filtered_iter).set_value_full(
       full_version_entry.value(), executor_ptr,
       b_migration_enabled, is_persistent());
    }

    if (b_caller_is_set_element)
      ct.try_eviction(*this);
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Implementation helper function for @ref set_value to handle
  /// full version set value calls.
  //////////////////////////////////////////////////////////////////////
  template<typename Filter, typename ValueParam>
  void
  set_value_impl(ValueParam&& val, edge_container& ct,
                 unsigned int version_id,
                 bool b_caller_is_set_element, bool b_migration_enabled,
                 bool b_expect_exist, std::true_type)
  {
    stapl_assert(version_id == 0, "Unexpected version_id (expected full)");

    // else, req == FULL, fulfill lower level requests if possible
    auto& full_version_entry = get_full_version(b_expect_exist);

    // Using a pointer to handle case when edge_container backs a
    // PG input port initialized prior to executor construction.
    // Notifications can't exist (as factory hasn't been called for setup
    // of internal consumers), but value may be set if flow already available.
    auto executor_ptr = ct.tg().executor_ptr();

    // If it's a basic type, if there is exactly one consumer or if we already
    // know for certain that there are no remote consumers, store the value in
    // the version entry object frame.  Otherwise, give ARMI the lifetime
    // management responsibilities for the value, in preparation for potential
    // inter-location sharing (costs one heap allocation + reference counting
    // overhead).
    if (is_basic<stored_value_t>::value
        || this->has_single_consumer()
        || (m_unknown_consumer_cnt==0
            && m_remote_version.empty_notifications()))
    {
      full_version_entry.set_value(
        std::forward<ValueParam>(val), executor_ptr,
        b_migration_enabled, is_persistent());
    }
    else
    {
      full_version_entry.set_value(
        make_immutable_shared<stored_value_t>(std::forward<ValueParam>(val)),
        executor_ptr, b_migration_enabled, is_persistent());
    }

    versions_iter_t filtered_iter = iterator_lookup_version(FILTERED);

    // val is subject to move above and may be emptied now. Use value stored
    // in full_version_entry to service requests below (which are initialized
    // by copy, for now).
    if (b_caller_is_set_element)
    {
      const bool b_moveable = m_unknown_consumer_cnt==0 && m_local_user_cnt==0;

      stapl_assert(!b_moveable || filtered_iter == m_versions.end(),
        "expected empty filtered notifier list");

      m_remote_version.set_value(
        ct, tid(), entry_storage_access<stored_value_t>(&full_version_entry),
        is_persistent(), b_moveable);
    }

    // use set_value_full so filtered_version applies filter before notifying.
    if (filtered_iter != m_versions.end())
    {
      down_cast<filtered_edge_versions_t&>(*filtered_iter).set_value_full(
        full_version_entry.value(), executor_ptr,
        b_migration_enabled, is_persistent());
    }

    if (b_caller_is_set_element)
      ct.try_eviction(*this);
 }

  //////////////////////////////////////////////////////////////////////
  /// @brief Implementation helper function for @ref set_value to handle
  /// filtered versions set value calls.
  //////////////////////////////////////////////////////////////////////
  template<typename Filter, typename ValueParam>
  void
  set_value_impl(ValueParam&& val, edge_container& ct,
                 unsigned int version_id,
                 bool b_caller_is_set_element, bool b_migration_enabled,
                 bool b_expect_exist, std::false_type)
  {
    stapl_assert(version_id > 0, "Unexpected version_id (expected filtered)");

    // Using a pointer to handle case when edge_container backs a
    // pg input port initialized prior to executor construction.
    // notifications can't exist (as factory hasn't been called for setup
    // of internal consumers), but value may be set if flow already available.
    auto executor_ptr = ct.tg().executor_ptr();

    get_filtered_versions(b_expect_exist). template set_value<Filter>(
      std::forward<ValueParam>(val), version_id, executor_ptr,
      b_migration_enabled, is_persistent(), b_expect_exist);
  }

  template<typename Filter>
  void set_value(immutable_shared<stored_value_t>&& wrapper,
                 edge_container& ct,
                 unsigned int version_id,
                 bool b_caller_is_set_element = false,
                 bool b_caller_is_migration   = false)
  {
    this->set_shared_value(wrapper, ct, version_id,
                           b_caller_is_set_element,
                           b_caller_is_migration);
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Used to trigger data flow in this entry for edges that can be
  /// serviced with the version of the value that has been passed.
  ///
  /// @param val The edge value that has been flowed.
  /// @param ct The edge container this entry is held by.
  /// @param version_id The version identifier for @p val.  Zero if the full
  ///   producer. Otherwise a filtered version with the identifier previously
  ///   assigned in @ref add_local_notifier.
  /// @param b_caller_is_set_element Denotes whether the caller of this
  ///   method is set_element (i.e., the original data flow directly from
  ///   the producer task).
  /// @param b_caller_is_migration Denotes this is called when moving a
  ///   predecessor value for a successor migrated task.  Guards assertions
  ///   checking previous existence of value (to verify protocol correctness).
  ///
  /// @todo Use is_moveable<T> metafunction from runtime to guard generation of
  ///   move code paths for basic types, etc.
  //////////////////////////////////////////////////////////////////////
  template<typename Filter, typename ValueParam>
  void set_value(ValueParam&& val,
                 edge_container& ct,
                 unsigned int version_id,
                 bool b_caller_is_set_element = false,
                 bool b_caller_is_migration   = false)
  {
    const bool b_migration_enabled = ct.tg().migration_enabled();

    stapl_assert(!b_caller_is_set_element || version_id == 0,
      "b_caller_is_set_element and version_id != FULL");

    stapl_assert(m_remote_version.empty_notifications()
                || b_caller_is_set_element,
      "Found pending remote notifications and I'm not the producer location!");

    // Either filtered or full means signal requests can be notified.
    if (!this->signal_set())
    {
      // Using a pointer to handle case when edge_container backs a
      // pg input port initialized prior to executor construction.
      // notifications can't exist (as factory hasn't been called for setup
      // of internal consumers), but value may be set if flow already available.
      auto executor_ptr = ct.tg().executor_ptr();

      this->set_signal(executor_ptr, b_migration_enabled);
    }
    else
      stapl_assert(this->is_persistent() || this->empty_signal_notifications(),
        "m_b_signal_flow_set with pending SIGNAL notifications");

    // When producer calls set_value via tg::processed, if no local consumers
    // exists yet, there isn't a version entry yet.  Also may not exist when
    // migrating a consume value during task migration.
    // Otherwise, we expect there to be one and will assert otherwise.
    const bool b_expect_exist =
      !(b_caller_is_set_element || b_caller_is_migration);

    this->set_value_impl<Filter>(
      std::forward<ValueParam>(val), ct, version_id,
      b_caller_is_set_element, b_migration_enabled, b_expect_exist,
      typename std::is_same<
        Filter, detail::df_identity<stored_value_t>>::type());
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Used to trigger data flow in this entry for edges that are
  /// void.
  ///
  /// @param ct The @p edge_container holding this entry.
  ///
  /// Redirect to general set_value with default constructed value (this type
  /// may be unknown in the calling context, as this is a virtual function that
  /// can be called through @p edge_entry_base.
  //////////////////////////////////////////////////////////////////////
  void set_value(edge_container& ct) final
  {
    this->set_value<detail::df_identity<stored_value_t>>(
      stored_value_t(), ct, 0, true);
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief The general evictability test for the edge_entry. Detects
  /// whether this entry is no longer needed on the location and
  /// can be removed.
  ///
  /// Verifies all consumers have been registered and notified (if this is the
  /// producer location) and that no local consumers are still using any version
  /// maintained by the entry.
  //////////////////////////////////////////////////////////////////////
  bool persistent_evictable(void) const
  {
    return this->m_unknown_consumer_cnt == 0
        && this->m_local_user_cnt == 0
        && empty_local_notifications()
        && m_remote_version.empty_notifications()
        && !m_remote_version.test_flushing();
  }

  ///////////////////////////////////////////////////////////////////////
  /// @brief Check whether this entry is no longer needed on the location and
  /// can be removed.
  ///
  /// Guarded to return false if the PARAGRAPH is persistent.  Eviction attempts
  /// in the edge container are disabled, allowing entry to remain until the
  /// destructor of the @p edge_container (as part of overall PARAGRAPH
  /// destruction).
  //////////////////////////////////////////////////////////////////////
  bool evictable(void) const
  {
    return this->persistent_evictable() && !this->is_persistent();
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Extract all state necessary to migrate the producer state from this
  /// entry and move it to new execution location.
  ///
  /// @param other A reference to an empty notifier list.
  /// Move all remote notifiers into this. @p edge_container::migrate_entry will
  /// forward these notifiers there after invoking @p directory::migrate.
  ///
  /// @param ct_unknown_consumer_cnt A reference to the edge container's global
  /// unknown consumer count.  Decrement with entry's current unknown count,
  /// as these are now another location's responsibility to track.
  ///
  /// @return The original successor count specified for the producer task.
  ///
  /// @sa edge_container::migrate_entry
  //////////////////////////////////////////////////////////////////////
  size_t migrate(remote_notifier_list_t& other, int& ct_unknown_consumer_cnt)
  {
    stapl_assert(!this->is_persistent(),
      "Tried to migrate persistent edge_entry");

    // Before migrating the task or notifiers, reset the entry as if the
    // producer was never here (i.e., set m_unknown_consumer_cnt to 0, and
    // clear remote notifications). This is because we can poll in the
    // asyncs (and could potentially receive the task back or more notifiers)
    // Save the total_consumer_cnt before resetting the entry.
    //
    stapl_assert(this->m_unknown_consumer_cnt != defer_spec,
      "migration not supported for delayed successor specification yet");

    // ***NOTE***
    //
    // The size() call on remote_notifications list is non-constant time.
    // It's linear with the number of notifications.  This saves us a word
    // in all cache entries, with the only cost being this lookup when we
    // migrate.  Could flip on constant time for migration enabled
    // edge_containers / PARAGRAPHs if we thought this was a real problem.
    //
    const size_t total_consumer_cnt =
      this->m_unknown_consumer_cnt + m_remote_version.notifications().size();

    // This cast can go if we make entry.m_unknown_consumer_cnt of type int.
    stapl_assert(ct_unknown_consumer_cnt >= (int) this->m_unknown_consumer_cnt,
      "edge_container's aggregate unknown consumers less than entry's");

    ct_unknown_consumer_cnt -= this->m_unknown_consumer_cnt;

    this->m_unknown_consumer_cnt = 0;

    stapl_assert(other.empty(), "expected empty other");

    other.swap(m_remote_version.notifications());

    return total_consumer_cnt;
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Reset data flow triggers prior to a persistent PARAGRAPH
  /// reinvocation.
  ///
  /// Reset the remote version list and all versions stored in the versions
  /// list.  Also invoke @p edge_entry_base::reset_values to reset the signal
  /// version list.
  //////////////////////////////////////////////////////////////////////
  void reset_values(void) final
  {
    edge_entry_base::reset_values();

    for (auto&& version : m_versions)
      version.clear_flow();

    m_remote_version.clear_flow();
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Return true if the full value of the producer task is currently
  /// available (i.e., data flow triggered) in this entry.
  //////////////////////////////////////////////////////////////////////
  bool full_value_set(void) const final
  {
    const versions_citer_t iter = this->iterator_lookup_version(FULL);

   if (iter == m_versions.end())
     return false;

   full_edge_version_entry_t const& full_version =
     down_cast<full_edge_version_entry_t const&>(*iter);

   stapl_assert(this->signal_set() || !full_version.test_flow(),
     "FULL set but not SIGNAL");

    return full_version.test_flow();
  }

private:
  /////////////////////////////////////////////////////////////////////
  /// @brief Scan version list and return a const_iterator to the version entry
  /// for given request_type value.
  ///
  /// @param req The request type (e.g., FILTERED, FULL) to search for in the
  /// versions list.
  ///
  /// This lookup may be preferable to the "get" version below, as it allows the
  /// caller to take different action based on whether or not entry exists.
  //////////////////////////////////////////////////////////////////////
  versions_citer_t iterator_lookup_version(edge_request_type req) const
  {
    return std::find_if(m_versions.begin(), m_versions.end(), compare_req(req));
  }

  /////////////////////////////////////////////////////////////////////
  /// @brief Scan version list and return a iterator to the version entry for
  /// given request_type value.
  ///
  /// @param req The request type (e.g., FILTERED, FULL) to search for in the
  /// versions list.
  ///
  /// This lookup may be preferable to the "get" version below, as it allows the
  /// caller to take different action based on whether or not entry exists.
  //////////////////////////////////////////////////////////////////////
  versions_iter_t iterator_lookup_version(edge_request_type req)
  {
    return std::find_if(m_versions.begin(), m_versions.end(), compare_req(req));
  }

  /////////////////////////////////////////////////////////////////////
  /// @brief Scan version list and return reference to the filtered
  /// version entry.  Create if non-existent.
  ///
  /// @param b_expected True if the caller expects the value be there
  /// (i.e., for correctness of protocol).  Guards conditional assert.
  //////////////////////////////////////////////////////////////////////
  filtered_edge_versions_t& get_filtered_versions(bool b_expected = false)
  {
    versions_iter_t iter = iterator_lookup_version(FILTERED);

    if (iter != m_versions.end())
      return down_cast<filtered_edge_versions_t&>(*iter);

    stapl_assert(!b_expected, "didn't find existing when b_expected");

    m_versions.push_front(*new filtered_edge_versions_t());

    return down_cast<filtered_edge_versions_t&>(*m_versions.begin());
  }

  /////////////////////////////////////////////////////////////////////
  /// @brief Scan version list and return const reference to the filtered
  /// version entry.
  ///
  /// All uses of this method expect the entry to be there. Assert if it isn't.
  //////////////////////////////////////////////////////////////////////
  filtered_edge_versions_t const& get_filtered_versions(void) const
  {
    stapl_assert(iterator_lookup_version(FILTERED) != m_versions.end(),
      "failed to find version entry");

    return down_cast<filtered_edge_versions_t const&>
      (*iterator_lookup_version(FILTERED));
  }

  /////////////////////////////////////////////////////////////////////
  /// @brief Scan version list and return reference to the full
  /// value entry. Create if non-existent.
  ///
  /// @param b_expected True if the caller expects the value be there
  /// (i.e., for correctness of protocol).  Guards conditional assert.
  //////////////////////////////////////////////////////////////////////
  full_edge_version_entry_t& get_full_version(bool b_expected = false)
  {
    versions_iter_t iter = iterator_lookup_version(FULL);

    if (iter != m_versions.end())
      return down_cast<full_edge_version_entry_t&>(*iter);

    stapl_assert(!b_expected, "didn't find existing when b_expected");

    m_versions.push_front(*new full_edge_version_entry_t());

    return down_cast<full_edge_version_entry_t&>(*m_versions.begin());
  }

  /////////////////////////////////////////////////////////////////////
  /// @brief Scan version list and return const reference to the full
  /// value entry.
  ///
  /// All uses of this method expect the entry to be there. Assert if it isn't.
  //////////////////////////////////////////////////////////////////////
  full_edge_version_entry_t const& get_full_version(void) const
  {
    stapl_assert(iterator_lookup_version(FULL) != m_versions.end(),
      "failed to find version entry");

    return down_cast<full_edge_version_entry_t const&>
      (*iterator_lookup_version(FULL));
  }

  //////////////////////////////////////////////////////////////////////
  /// @brief Called when eviction is checked polymorphically through
  /// @p edge_entry_base::evictable. Redirect to @p edge_entry::evictable.
  ///
  /// @sa evictable.
  //////////////////////////////////////////////////////////////////////
  bool poly_evictable(void) const final
  {
    return this->evictable();
  }
}; // class edge_entry

//////////////////////////////////////////////////////////////////////
/// @brief Provides trivial iterator interface to an @ref edge_entry
/// pointer and avoids serialization of object.  Used in data flow
/// fire (@ref edge_container::receive_value) to elide relookup in
/// value cache.
///
/// @todo Remove usage if/when ARMI allows bitwise() style annotation
/// on arguments passed to an RMI (to avoid serialization of pointed
/// to object when all that is desired is the address).
//////////////////////////////////////////////////////////////////////
template<typename T>
class edge_entry_wrapper
{
private:
  edge_entry<T>* m_edge_entry_ptr;

public:
  edge_entry_wrapper(edge_entry<T>* edge_entry_ptr)
    : m_edge_entry_ptr(edge_entry_ptr)
  { }

  edge_entry<T>* operator->()
  { return m_edge_entry_ptr; }

  edge_entry<T>& operator*()
  { return *m_edge_entry_ptr; }

  void define_type(typer& t)
  { t.member(bitwise(t)); }

  edge_entry<T>* pointer(void)
  { return m_edge_entry_ptr; }

}; // class edge_entry_wrapper

} // namespace detail

} // namespace stapl

#endif // ifndef STAPL_PARAGRAPH_EDGE_ENTRY_HPP


text:
/** Project: Boost-Program-Options
 *
 *  Author: Jose Fernando Lopez Fernandez
 *
 *  Date: 04-May-2018
 *
 *  Description: For this project, I'm supposing I'm trying to parse options
 *  from the point of view of a project meta-generator. These
 *  will be the targeted use-cases of my experimentation.
 *
 */

#include <boost/chrono.hpp>
#include <boost/optional.hpp>
#include <boost/program_options.hpp>
#include <boost/ratio.hpp>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

/** There will be a need to combine the values from the command line and
 *  configuration file. For example, the optimization level specified on the
 *  command line should override the value from the config file. On the other
 *  hand, include paths should be combined.
 *
 */

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vector) {
	std::copy(vector.begin(), vector.end(), std::ostream_iterator<T>(os, " "));

	return os;
}

const std::string DEFAULT_PROJECT_LANGUAGE = "C";

namespace Options = boost::program_options;

using OptsDescription = boost::program_options::options_description;

int main(int argc, char *argv[])
{
	try {
		std::string configFileName;

		int optimizationLevel = 0;

		boost::optional<std::string> projectLanguage = boost::make_optional<std::string>("");

		// Declare a group of options that will be allowed only on the command line
		OptsDescription generic("Generic options");
		generic.add_options()
			("version,v", "Print program version")
			("help,h", "Print help dialog");

		// Declare a group of options that will be allowed both on the command
		// line and in the configuration file
		OptsDescription config("Configuration");
		config.add_options()
			("config,c", Options::value<std::string>(&configFileName), "Configuration file")
			("language,l", Options::value(&projectLanguage), "Project language")
			("optimization-level,O", Options::value<int>(&optimizationLevel)->default_value(0), "Optimization level")
			("include-path,I", Options::value<std::vector<std::string>>()->composing(), "Include path")
			("classes", Options::value<std::vector<std::string>>(), "Classes");

		// Hidden options which will be allowed both on the command line and
		// in the configuration file, but which will not be shown to the user
		OptsDescription hidden("Hidden options");
		hidden.add_options()
			("input-file", Options::value<std::vector<std::string>>(), "Input file(s)");

		OptsDescription commandLineOptions;
		commandLineOptions.add(generic).add(config).add(hidden);

		OptsDescription configFileOptions;
		configFileOptions.add(config).add(hidden);

		OptsDescription visible("Allowed options");
		visible.add(generic).add(config);

		Options::positional_options_description positionalOptsDesc;
		positionalOptsDesc.add("classes", -1);

		Options::variables_map variablesMap;
		Options::store(Options::command_line_parser(argc, argv).
			options(commandLineOptions).positional(positionalOptsDesc).run(), variablesMap);
		Options::notify(variablesMap);

		if (variablesMap.count("config")) {
			std::ifstream ifs(configFileName.c_str());
			if (!ifs) {
				std::cerr << "[Error]: Could not open config file...\n";

				exit(EXIT_FAILURE);
			} else {
				Options::store(Options::parse_config_file(ifs, configFileOptions), variablesMap);
				Options::notify(variablesMap);
			}
		}

		if (variablesMap.count("help")) {
			std::cout << "Usage: Boost-Program-Options [options]\n"
				<< commandLineOptions
				<< "\n";

			return EXIT_SUCCESS;
		}

		if (variablesMap.count("version")) {
			std::cout << "<Project Name>: Version "
				<< "0.0.1"
				<< ".\n";

			return EXIT_SUCCESS;
		}

		if (variablesMap.count("include-path")) {
			std::cout << "Include path: "
				<< variablesMap["include-path"].as<std::vector<std::string>>()
				<< "\n";
		}

		if (variablesMap.count("input-file")) {
			std::cout << "Input file(s): "
				<< variablesMap["input-file"].as<std::vector<std::string>>()
				<< "\n";
		}

		if (variablesMap.count("classes")) {
			std::cout << "Classes: "
				<< variablesMap["classes"].as<std::vector<std::string>>()
				<< "\n";
		}

		if (!(*projectLanguage).empty()) {
			std::cout << "Project Language: "
				<< *projectLanguage
				<< "\n";
		} else {
			std::cout << "Project language set to default: "
				<< (*projectLanguage = DEFAULT_PROJECT_LANGUAGE)
				<< ".\n";
		}
	} catch (std::exception& e) {
		std::cerr << "[Error]: " << e.what() << "\n";
	}

	return EXIT_SUCCESS;
}

max_stars_repo_name: lopezfjose/Boost-Program-Options

max_issues_repo_name: lopezfjose/Boost-Program-Options

max_forks_repo_name: lopezfjose/Boost-Program-Options


text:
/*!
 * \file Property.hpp
 * \brief
 */

#ifndef PROPERTY_HPP_
#define PROPERTY_HPP_

#include "Common/Utils.hpp"

#include <string>

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

#include <boost/preprocessor/list.hpp>
#include <boost/preprocessor/tuple/to_list.hpp>

#include <typeinfo>
#include <vector>
#include <sstream>

#include <iostream>

#define INSIDE_IF(_, PREFIX, VAL) if(s == #VAL) return PREFIX##VAL;
#define INSIDE_CASE(_, PREFIX, VAL) case PREFIX##VAL: return #VAL;

#define GENERATE_ENUM_TRANSLATOR(TranslatorName, TranslatorType, Elems, Prefix) \
	class TranslatorName { \
	public:\
		static TranslatorType fromStr(const std::string & s) {\
			BOOST_PP_LIST_FOR_EACH(INSIDE_IF, Prefix, Elems); \
			return BOOST_PP_CAT(Prefix, BOOST_PP_LIST_FIRST(Elems));\
		}\
		\
		static std::string toStr(TranslatorType t) {\
			switch(t) {\
			BOOST_PP_LIST_FOR_EACH(INSIDE_CASE, Prefix, Elems); \
			default: return BOOST_PP_STRINGIZE(BOOST_PP_LIST_FIRST(Elems));\
			}\
		}\
	};

//#define ADD_CONSTRAINT(_, Property, Value) Property.addConstraint(#Value);
//#define PROP_ADD_COMBO_ITEMS(Property, Elems) BOOST_PP_LIST_FOR_EACH(ADD_CONSTRAINT, Property, Elems)
#define ADD_COMBO_ITEMS_CONSTRAINT(_, Property, Value) Property.setNamedConstraint(BOOST_PP_STRINGIZE(option##_), std::string(#Value), true);
#define PROP_ADD_COMBO_ITEMS(Property, Elems) BOOST_PP_LIST_FOR_EACH(ADD_COMBO_ITEMS_CONSTRAINT, Property, Elems)

namespace Base {

template<typename T>
class LexicalTranslator {
public:
	static std::string toStr(const T & val) {
		try {
			return boost::lexical_cast<std::string>(val);
		} catch(...) {
			std::stringstream ss;
			ss << val;
			return ss.str();
		}
		return "";
	}

	static T fromStr(const std::string & str) {
		try {
			return boost::lexical_cast<T>(str);
		} catch (...) {
			std::stringstream ss(str);
			T ret;
			ss >> ret;
			return ret;
		}
		return T();
	}
};

class PropertyInformer {
public:
	template<typename U>
	void set(const std::string & name, const U & value) {
		m_props[name] = LexicalTranslator<U>::toStr(value);
	}

	template<typename U>
	U get(const std::string & name) {
		return LexicalTranslator<U>::fromStr(m_props[name]);
	}
protected:
	std::map<std::string, std::string> m_props;
};

template<class T>
class PropertyInformerHolder {
public:
	static PropertyInformer * get() {
		static PropertyInformer * pi = new PropertyInformer;
		return pi;
	}
};

class PropertyInterface {
public:

	PropertyInterface(const std::string & n) :
			name_(n), persistent(true), m_tool_tip(n) {
	}

	virtual ~PropertyInterface() {}

	/*
	 template <typename T>
	 T get() {
	 return &((T*)internalGet());
	 }

	 template <typename T>
	 T set(const T & t) {
	 internalSet(&t);
	 return t;
	 }*/

	bool isPersistent() {
		return persistent;
	}

	virtual std::string store() = 0;

	virtual void retrieve(const std::string & str) = 0;

	const std::string & name() const {
		return name_;
	}

	virtual std::string type() const = 0;

	std::string toolTip() {
		return m_tool_tip;
	}

	void setToolTip(const std::string & tool_tip) {
		m_tool_tip = tool_tip;
	}

	//MARK_DEPRECATED("Use setNamedConstraint instead")
	void addConstraint(const std::string & str) {
		m_constraints.push_back(str);
	}

	int countConstraints() const {
		return m_constraints.size();
	}

	std::string getConstraint(int i) const {
		return m_constraints[i];
	}

private:
	/// property name
	std::string name_;

	bool persistent;

	std::string m_tool_tip;

	std::vector<std::string> m_constraints;
};

/*!
 *
 */
template<class T, class Translator = LexicalTranslator<T> >
class Property: public PropertyInterface {
public:
	Property(const std::string& name, const T & initializer = T(),
			std::string type = typeid(T).name()) :
			PropertyInterface(name), data(initializer), m_type(type) {
	}

	Property(const std::string& name, boost::function<void(T, T)> callback,
			const T & initializer = T(), std::string type = typeid(T).name()) :
			PropertyInterface(name), data(initializer), m_onChange(callback),
			m_type(type) {
	}

	void setCallback(boost::function<void(T, T)> callback) {
		m_onChange = callback;
	}

	/*!
	 * Access with function call syntax
	 */
	T operator()() const {
		return data;
	}

	T operator()(T const & value) {
		data = value;
		return data;
	}

	/*!
	 * Return value
	 *
	 * @return current value
	 */
	operator T() const {
		return data;
	}

	/*!
	 * Set new value.
	 *
	 * @param value value to be set
	 * @return value
	 */
	T operator=(T const & value) {
		data = value;
		return data;
	}

	bool operator==(T const & value) {
		return data == value;
	}

	bool operator!=(T const & value) {
		return data != value;
	}

	/*!
	 * Return string representation of current value
	 *
	 * @return string representation of current value.
	 */
	virtual std::string store() {
		return Translator::toStr(data);
	}

	/*!
	 * Retrieve value from it's string representation.
	 *
	 * @param str string to retrieve value from.
	 */
	virtual void retrieve(const std::string & str) {
		T old = data;
		data = Translator::fromStr(str);
		if (m_onChange)
			m_onChange(old, data);
	}

	std::string type() const {
		return m_type;
	}

	/// Might be useful for template deductions
	typedef T value_type;

	friend std::ostream & operator<<(std::ostream & os, const Property & prop) {
		os << prop.data;
		return os;
	}

	template<typename U>
	void setNamedConstraint(const std::string & name, const U & value,
			bool builtin = false) {
		//if (builtin) {
		m_named_constraints[name] = LexicalTranslator<U>::toStr(value);
		//} else {
		//m_named_constraints[name] = Translator::toStr(value);
		//}
	}

	template<typename U>
	U getNamedConstraint(const std::string & name, bool builtin = false) {
		if (builtin)
			return LexicalTranslator<U>::fromStr(m_named_constraints[name]);
		else
			return Translator::fromStr(m_named_constraints[name]);
	}

protected:
	/// actual data
	T data;

	/// callback on data change
	boost::function<void(T, T)> m_onChange;

	std::string m_type;

	std::map<std::string, std::string> m_named_constraints;
};

}

#endif /* PROPERTY_HPP_ */

max_stars_repo_name: mlaszko/DisCODe

max_issues_repo_name: mlaszko/DisCODe

max_forks_repo_name: mlaszko/DisCODe