text:
//
// Copyright 2018 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
//    names, trademarks, service marks, or product names of the Licensor
//    and its affiliates, except as required to comply with Section 4(c) of
//    the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "pxr/pxr.h"
#include "pxr/base/arch/attributes.h"
#include "pxr/base/arch/pragmas.h"
#include "pxr/base/arch/vsnprintf.h"
#include "pxr/base/tf/fileUtils.h"
#include "pxr/base/tf/ostreamMethods.h"
#include "pxr/base/tf/patternMatcher.h"
#include "pxr/base/tf/stringUtils.h"
#include "pxr/base/tf/scopeDescription.h"
// copyUtils.h pulls in boost/any.hpp via tf/diagnostic, and this winds up
// generating an erroneous maybe-uninitialized warning on certain gcc & boost
// versions, so we disable that diagnostic around this include.
ARCH_PRAGMA_PUSH
ARCH_PRAGMA_MAYBE_UNINITIALIZED
#include "pxr/usd/sdf/copyUtils.h"
ARCH_PRAGMA_POP
#include "pxr/usd/sdf/fileFormat.h"
#include "pxr/usd/sdf/layer.h"
#include "pxr/usd/sdf/primSpec.h"
#include "pxr/usd/sdf/types.h"
#include "pxr/usd/sdf/textFileFormat.h"

#include <boost/program_options.hpp>

#include <cstdio>
#include <cstdarg>
#include <functional>
#include <iostream>
#include <string>
#include <utility>
#include <unordered_map>
#include <unordered_set>
#include <vector>

PXR_NAMESPACE_OPEN_SCOPE

using std::string;
using std::vector;
using std::ostream;
using std::pair;
using std::unordered_map;
using std::unordered_set;

// A file format for the human readable "pseudoLayer" output.  We use this so
// that the terse human-readable output we produce is not a valid layer nor may
// be mistaken for one.
class SdfFilterPseudoFileFormat : public SdfTextFileFormat
{
private:
    SDF_FILE_FORMAT_FACTORY_ACCESS;

public:
    SdfFilterPseudoFileFormat(string description="<< human readable >>")
        : SdfTextFileFormat(TfToken("pseudosdf"),
                            TfToken(description),
                            SdfTextFileFormatTokens->Target) {}
private:

    virtual ~SdfFilterPseudoFileFormat() {}
};

TF_REGISTRY_FUNCTION(TfType)
{
    SDF_DEFINE_FILE_FORMAT(SdfFilterPseudoFileFormat, SdfTextFileFormat);
}

namespace {

// the basename of the executable.
string progName;

// print/error utilities.
void VErr(char const *fmt, va_list ap) {
    fprintf(stderr, "%s: Error - %s\n", progName.c_str(),
            ArchVStringPrintf(fmt, ap).c_str());
}

void VPrint(char const *fmt, va_list ap) { vprintf(fmt, ap); }

void Err(char const *fmt, ...) ARCH_PRINTF_FUNCTION(1, 2);
void Err(char const *fmt, ...) {
    va_list ap; va_start(ap, fmt); VErr(fmt, ap); va_end(ap);
}

void ErrExit(char const *fmt, ...) ARCH_PRINTF_FUNCTION(1, 2);
void ErrExit(char const *fmt, ...) {
    va_list ap; va_start(ap, fmt); VErr(fmt, ap); va_end(ap);
    exit(1);
}

void Print(char const *fmt, ...) ARCH_PRINTF_FUNCTION(1, 2);
void Print(char const *fmt, ...) {
    va_list ap; va_start(ap, fmt); VPrint(fmt, ap); va_end(ap);
}

// The sorting key for 'outline' output.
enum SortKey {
    SortByPath,
    SortByField
};

std::ostream &
operator<<(std::ostream &os, SortKey key)
{
    if (key == SortByPath) {
        return os << "path";
    }
    else if (key == SortByField) {
        return os << "field";
    }
    TF_CODING_ERROR("Invalid value for SortKey (%d)",
                    static_cast<int>(key));
    return os << "invalid";
}

std::istream &
operator>>(std::istream &is, SortKey &key)
{
    std::string txt;
    is >> txt;
    if (txt == "path") {
        key = SortByPath;
    }
    else if (txt == "field") {
        key = SortByField;
    }
    else {
        is.setstate(std::ios_base::failbit);
    }
    return is;
}

// An enum representing the type of output to produce.
enum OutputType {
    OutputValidity,     // only check file validity by reading all values.
    OutputSummary,      // report a brief summary with file statistics.
    OutputOutline,      // report as an outilne, either by path or by field.
    OutputPseudoLayer,  // report as human readable text, as close to a
                        // valid layer as possible
    OutputLayer         // produce a valid layer as output.
};

std::ostream &
operator<<(std::ostream &os, OutputType outputType)
{
    if (outputType == OutputValidity) {
        return os << "validity";
    }
    else if (outputType == OutputSummary) {
        return os << "summary";
    }
    else if (outputType == OutputOutline) {
        return os << "outline";
    }
    else if (outputType == OutputPseudoLayer) {
        return os << "pseudoLayer";
    }
    else if (outputType == OutputLayer) {
        return os << "layer";
    }
    TF_CODING_ERROR("Invalid value for OutputType (%d)",
                    static_cast<int>(outputType));
    return os << "invalid";
}

std::istream &
operator>>(std::istream &is, OutputType &outputType)
{
    std::string txt;
    is >> txt;
    if (txt == "validity") {
        outputType = OutputValidity;
    }
    else if (txt == "summary") {
        outputType = OutputSummary;
    }
    else if (txt == "outline") {
        outputType = OutputOutline;
    }
    else if (txt == "pseudoLayer") {
        outputType = OutputPseudoLayer;
    }
    else if (txt == "layer") {
        outputType = OutputLayer;
    }
    else {
        is.setstate(std::ios_base::failbit);
    }
    return is;
}

// We use this structure to represent all the parameters for reporting.  We fill
// this using command-line args.
struct ReportParams
{
    std::shared_ptr<TfPatternMatcher> pathMatcher;
    std::shared_ptr<TfPatternMatcher> fieldMatcher;

    OutputType outputType;
    std::string outputFile;
    std::string outputFormat;

    std::vector<double> literalTimes;
    std::vector<std::pair<double, double>> timeRanges;
    double timeTolerance;

    SortKey sortKey = SortByPath;
    int64_t arraySizeLimit = -1;
    int64_t timeSamplesSizeLimit = -1;
    bool showValues = true;
};

// Summary statistics for 'summary' output.
struct SummaryStats
{
    size_t numSpecs = 0;
    size_t numPrimSpecs = 0;
    size_t numPropertySpecs = 0;
    size_t numFields = 0;
    size_t numSampleTimes = 0;
};

// Parse times and time ranges in timeSpecs, throw an exception if something
// goes wrong.
void
ParseTimes(vector<string> const &timeSpecs,
           vector<double> *literalTimes,
           vector<pair<double, double>> *timeRanges)
{
    for (auto const &spec: timeSpecs) {
        try {
            if (TfStringContains(spec, "..")) {
                auto elts = TfStringSplit(spec, "..");
                if (elts.size() != 2) {
                    throw std::invalid_argument(
                        TfStringPrintf("invalid time syntax '%s'",
                                       spec.c_str()));
                }
                timeRanges->emplace_back(boost::lexical_cast<double>(elts[0]),
                                         boost::lexical_cast<double>(elts[1]));
            } else {
                literalTimes->emplace_back(boost::lexical_cast<double>(spec));
            }
        } catch (boost::bad_lexical_cast const &) {
            throw std::invalid_argument(
                TfStringPrintf("invalid time syntax '%s'", spec.c_str()));
        }
    }
    sort(literalTimes->begin(), literalTimes->end());
    literalTimes->erase(unique(literalTimes->begin(), literalTimes->end()),
                        literalTimes->end());
    sort(timeRanges->begin(), timeRanges->end());
    timeRanges->erase(unique(timeRanges->begin(), timeRanges->end()),
                      timeRanges->end());
}

// Find all the paths in layer that match, or all paths if matcher is null.
std::vector<SdfPath>
CollectMatchingSpecPaths(SdfLayerHandle const &layer,
                         TfPatternMatcher const *matcher)
{
    std::vector<SdfPath> result;
    layer->Traverse(SdfPath::AbsoluteRootPath(),
                    [&result, &matcher](SdfPath const &path) {
                        if (!matcher || matcher->Match(path.GetString()))
                            result.push_back(path);
                    });
    return result;
}

// Find all the fields for the given path that match, or all fields if matcher
// is null.
std::vector<TfToken>
CollectMatchingFields(SdfLayerHandle const &layer,
                      SdfPath const &path,
                      TfPatternMatcher const *matcher)
{
    std::vector<TfToken> fields = layer->ListFields(path);
    fields.erase(remove_if(fields.begin(), fields.end(),
                           [&matcher](TfToken const &f) {
                               return matcher && !matcher->Match(f.GetString());
                           }),
                 fields.end());
    return fields;
}

// Closeness check with relative tolerance.
bool IsClose(double a, double b, double tol)
{
    double absDiff = fabs(a-b);
    return absDiff <= fabs(tol*a) || absDiff < fabs(tol*b);
}

// Get a suitable value for the report specified by p.  In particular, for
// non-layer output, make a value that shows only array type & size for large
// arrays.
VtValue
GetReportValue(VtValue const &value, ReportParams const &p)
{
    if (p.outputType != OutputLayer &&
        p.arraySizeLimit >= 0 &&
        value.IsArrayValued() &&
        value.GetArraySize() > static_cast<uint64_t>(p.arraySizeLimit)) {
        return VtValue(
            SdfHumanReadableValue(
                TfStringPrintf(
                    "%s[%zu]",
                    ArchGetDemangled(value.GetElementTypeid()).c_str(),
                    value.GetArraySize())));
    }
    return value;
}

// Get a suitable value for timeSamples for the report specified by p.  In
// particular, for non-layer output, make a value that shows number of samples
// and their time range.
VtValue
GetReportTimeSamplesValue(SdfLayerHandle const &layer,
                          SdfPath const &path, ReportParams const &p)
{
    bool takeAllTimes = p.literalTimes.empty() && p.timeRanges.empty();
    auto times = layer->ListTimeSamplesForPath(path);
    std::vector<double> selectedTimes;
    selectedTimes.reserve(times.size());

    if (takeAllTimes) {
        selectedTimes.assign(times.begin(), times.end());
    }
    else {
        for (auto time: times) {
            // Check literalTimes.
            auto rng = equal_range(
                p.literalTimes.begin(), p.literalTimes.end(), time,
                [&p](double a, double b)  {
                    return IsClose(a, b, p.timeTolerance) ? false : a < b;
                });
            if (rng.first != rng.second)
                selectedTimes.push_back(time);

            // Check ranges.
            for (auto const &range: p.timeRanges) {
                if (range.first <= time && time <= range.second)
                    selectedTimes.push_back(time);
            }
        }
    }

    if (selectedTimes.empty())
        return VtValue();

    SdfTimeSampleMap result;

    VtValue val;
    if (p.outputType != OutputLayer &&
        p.timeSamplesSizeLimit >= 0 &&
        selectedTimes.size() > static_cast<uint64_t>(p.timeSamplesSizeLimit)) {
        return VtValue(
            SdfHumanReadableValue(
                TfStringPrintf("%zu samples in [%s, %s]",
                               times.size(),
                               TfStringify(*times.begin()).c_str(),
                               TfStringify(*(--times.end())).c_str())
                )
            );
    }
    else {
        for (auto time: selectedTimes) {
            TF_VERIFY(layer->QueryTimeSample(path, time, &val));
            result[time] = GetReportValue(val, p);
        }
    }
    return VtValue(result);
}

// Get a suitable value for the report specified by p.  In particular, for
// non-layer output, make a value that shows only array type & size for large
// arrays or number of time samples and time range for large timeSamples.
VtValue
GetReportFieldValue(SdfLayerHandle const &layer,
                           SdfPath const &path, TfToken const &field,
                           ReportParams const &p)
{
    VtValue result;
    // Handle timeSamples specially:
    if (field == SdfFieldKeys->TimeSamples) {
        result = GetReportTimeSamplesValue(layer, path, p);
    } else {
        TF_VERIFY(layer->HasField(path, field, &result));
        result = GetReportValue(result, p);
    }
    return result;
}

// Produce the 'outline' output report by path.
void
GetReportByPath(SdfLayerHandle const &layer,
                ReportParams const &p,
                std::vector<std::string> &report)
{
    std::vector<SdfPath> paths =
        CollectMatchingSpecPaths(layer, p.pathMatcher.get());
    sort(paths.begin(), paths.end());
    for (auto const &path: paths) {
        SdfSpecType specType = layer->GetSpecType(path);
        report.push_back(
            TfStringPrintf(
                "<%s> : %s",
                path.GetText(), TfStringify(specType).c_str()));

        std::vector<TfToken> fields =
            CollectMatchingFields(layer, path, p.fieldMatcher.get());
        if (fields.empty())
            continue;
        for (auto const &field: fields) {
            if (p.showValues) {
                report.push_back(
                    TfStringPrintf(
                        "  %s: %s", field.GetText(),
                        TfStringify(GetReportFieldValue(
                                        layer, path, field, p)).c_str())
                    );
            } else {
                report.push_back(TfStringPrintf("  %s", field.GetText()));
            }
        }
    }
}

// Produce the 'outline' output report by field.
void
GetReportByField(SdfLayerHandle const &layer,
                 ReportParams const &p,
                 std::vector<std::string> &report)
{
    std::vector<SdfPath> paths =
        CollectMatchingSpecPaths(layer, p.pathMatcher.get());
    std::unordered_map<
        std::string, std::vector<std::string>> pathsByFieldString;
    std::unordered_set<std::string> allFieldStrings;
    sort(paths.begin(), paths.end());
    for (auto const &path: paths) {
        std::vector<TfToken> fields =
            CollectMatchingFields(layer, path, p.fieldMatcher.get());
        if (fields.empty())
            continue;
        for (auto const &field: fields) {
            std::string fieldString;
            if (p.showValues) {
                fieldString = TfStringPrintf(
                    "%s: %s", field.GetText(),
                    TfStringify(GetReportFieldValue(
                                    layer, path, field, p)).c_str());
            } else {
                fieldString = TfStringPrintf("%s", field.GetText());
            }
            pathsByFieldString[fieldString].push_back(
                TfStringPrintf("  <%s>", path.GetText()));
            allFieldStrings.insert(fieldString);
        }
    }
    std::vector<std::string>
        fsvec(allFieldStrings.begin(), allFieldStrings.end());
    sort(fsvec.begin(), fsvec.end());

    for (auto const &fs: fsvec) {
        report.push_back(fs);
        auto const &ps = pathsByFieldString[fs];
        report.insert(report.end(), ps.begin(), ps.end());
    }
}

// Compute and return the summary statistics for the given layer.
SummaryStats
GetSummaryStats(SdfLayerHandle const &layer)
{
    SummaryStats stats;
    layer->Traverse(
        SdfPath::AbsoluteRootPath(), [&stats, &layer](SdfPath const &path) {
            ++stats.numSpecs;
            stats.numPrimSpecs += path.IsPrimPath();
            stats.numPropertySpecs += path.IsPropertyPath();
            stats.numFields += layer->ListFields(path).size();
        });
    stats.numSampleTimes = layer->ListAllTimeSamples().size();
    return stats;
}

// Utility function to filter a layer by the params p.  This copies fields,
// replacing large arrays and timeSamples with human readable values if
// appropriate, and skipping paths and fields that do not match the matchers in
// p.
void
FilterLayer(SdfLayerHandle const &inLayer,
            SdfLayerHandle const &outLayer,
            ReportParams const &p)
{
    namespace ph = std::placeholders;
    auto copyValueFn = [&p](
        SdfSpecType specType, TfToken const &field,
        SdfLayerHandle const &srcLayer, const SdfPath& srcPath, bool fieldInSrc,
        const SdfLayerHandle& dstLayer, const SdfPath& dstPath, bool fieldInDst,
        boost::optional<VtValue> *valueToCopy) {

        if (!p.fieldMatcher || p.fieldMatcher->Match(field.GetString())) {
            *valueToCopy = GetReportFieldValue(
                srcLayer, srcPath, field, p);
            return !(*valueToCopy)->IsEmpty();
        }
        else {
            return false;
        }
    };

    vector<SdfPath> paths =
        CollectMatchingSpecPaths(inLayer, p.pathMatcher.get());
    for (auto const &path: paths) {
        if (path == SdfPath::AbsoluteRootPath() ||
            path.IsPrimOrPrimVariantSelectionPath()) {
            SdfPrimSpecHandle outPrim = SdfCreatePrimInLayer(outLayer, path);
            SdfCopySpec(inLayer, path, outLayer, path,
                        copyValueFn,
                        std::bind(SdfShouldCopyChildren,
                                  std::cref(path), std::cref(path),
                                  ph::_1, ph::_2, ph::_3, ph::_4, ph::_5,
                                  ph::_6, ph::_7, ph::_8, ph::_9));
        }
    }
}

// Attempt to validate a layer by reading all field values from all paths.
void
Validate(SdfLayerHandle const &layer, ReportParams const &p,
         string &report)
{
    TfErrorMark m;
    TF_DESCRIBE_SCOPE("Collecting paths in @%s@",
                      layer->GetIdentifier().c_str());
    vector<SdfPath> paths;
    layer->Traverse(SdfPath::AbsoluteRootPath(),
                    [&paths, layer](SdfPath const &path) {
                        TF_DESCRIBE_SCOPE(
                            "Collecting path <%s> in @%s@",
                            path.GetText(), layer->GetIdentifier().c_str());
                        paths.push_back(path);
                    });
    sort(paths.begin(), paths.end());
    for (auto const &path: paths) {
        TF_DESCRIBE_SCOPE("Collecting fields for <%s> in @%s@",
                     path.GetText(), layer->GetIdentifier().c_str());
        vector<TfToken> fields = layer->ListFields(path);
        if (fields.empty())
            continue;
        for (auto const &field: fields) {
            VtValue value;
            if (field == SdfFieldKeys->TimeSamples) {
                // Pull each sample value individually.
                TF_DESCRIBE_SCOPE(
                    "Getting sample times for '%s' on <%s> in @%s@",
                    field.GetText(), path.GetText(),
                    layer->GetIdentifier().c_str());
                auto times = layer->ListTimeSamplesForPath(path);

                for (auto time: times) {
                    TF_DESCRIBE_SCOPE("Getting sample value at time "
                                      "%f for '%s' on <%s> in @%s@",
                                      time, field.GetText(), path.GetText(),
                                      layer->GetIdentifier().c_str());
                    layer->QueryTimeSample(path, time, &value);
                }
            } else {
                // Just pull value.
                TF_DESCRIBE_SCOPE("Getting value for '%s' on <%s> in @%s@",
                                  field.GetText(), path.GetText(),
                                  layer->GetIdentifier().c_str());
                layer->HasField(path, field, &value);
            }
        }
    }
    report += m.IsClean() ? "OK" : "ERROR";
}

// Output helper struct.  Manages fclosing the file handle, and appending output
// for multi-layer inputs.
struct OutputFile
{
    struct Closer {
        void operator()(FILE *f) const {
            if (f && f != stdout) {
                fclose(f);
            }
        }
    };
    explicit OutputFile(ReportParams const &p) {
        if (!p.outputFile.empty()) {
            if (p.outputType != OutputLayer) {
                _file.reset(fopen(p.outputFile.c_str(), "a"));
            }
        } else {
            _file.reset(stdout);
        }
    }
    void Write(string const &text) const {
        fputs(text.c_str(), _file.get());
    }
    std::unique_ptr<FILE, Closer> _file;
};

// Top level processing function; dispatches to various output implementations.
void Process(SdfLayerHandle layer, ReportParams const &p)
{
    OutputFile output(p);
    if (p.outputType == OutputValidity) {
        std::string validateText;
        Validate(layer, p, validateText);
        output.Write(
            TfStringPrintf(
                "@%s@ - %s\n",
                layer->GetIdentifier().c_str(),
                validateText.c_str()));
    }
    else if (p.outputType == OutputSummary) {
        auto stats = GetSummaryStats(layer);
        output.Write(
            TfStringPrintf(
                "@%s@\n"
                "  %zu specs, %zu prim specs, %zu property specs, %zu fields, "
                "%zu sample times\n",
                layer->GetIdentifier().c_str(),
                stats.numSpecs, stats.numPrimSpecs, stats.numPropertySpecs,
                stats.numFields, stats.numSampleTimes));
    }
    else if (p.outputType == OutputOutline) {
        vector<string> report;
        if (p.sortKey == SortByPath) {
            GetReportByPath(layer, p, report);
        }
        else if (p.sortKey == SortByField) {
            GetReportByField(layer, p, report);
        }
        TfStringPrintf("@%s@\n", layer->GetIdentifier().c_str());
        for (string const &line: report) {
            output.Write(line);
            output.Write("\n");
        }
    }
    else if (p.outputType == OutputPseudoLayer ||
             p.outputType == OutputLayer) {
        // Make the layer and copy into it, then export.
        SdfLayerRefPtr outputLayer;
        SdfFileFormatConstRefPtr fmt;
        if (p.outputType == OutputPseudoLayer) {
            fmt = TfCreateRefPtr(
                new SdfFilterPseudoFileFormat(
                    TfStringPrintf("from @%s@",
                                   layer->GetIdentifier().c_str())));
            outputLayer = SdfLayer::CreateAnonymous(".pseudosdf", fmt);
        } else {
            SdfLayer::FileFormatArguments formatArgs;
            if (!p.outputFormat.empty()) {
                formatArgs["format"] = p.outputFormat;
            }
            outputLayer = !p.outputFile.empty() ?
                SdfLayer::CreateNew(
                    p.outputFile, /*realPath=*/string(), formatArgs) :
                SdfLayer::CreateAnonymous(
                    p.outputFormat.empty() ? string() :
                    TfStringPrintf(".%s", p.outputFormat.c_str()));
        }

        // Generate the layer content.
        FilterLayer(layer, outputLayer, p);

        // If this layer is anonymous, it means we're writing to stdout.
        if (outputLayer->IsAnonymous()) {
            string txt;
            outputLayer->ExportToString(&txt);
            output.Write(txt);
        } else {
            outputLayer->Save();
        }
    }
}

} // anon

PXR_NAMESPACE_CLOSE_SCOPE

int
main(int argc, char const *argv[])
{
    namespace po = boost::program_options;
    PXR_NAMESPACE_USING_DIRECTIVE

    progName = TfGetBaseName(argv[0]);

    string pathRegex = ".*", fieldRegex = ".*";
    OutputType outputType = OutputOutline;
    string outputFile, outputFormat;
    vector<string> timeSpecs;
    vector<string> inputFiles;
    vector<double> literalTimes;
    vector<pair<double, double>> timeRanges;
    double timeTolerance = 1.25e-4; // ugh -- chosen to print well in help.
    SortKey sortKey = SortByPath;
    int64_t arraySizeLimit = -2;
    int64_t timeSamplesSizeLimit = -2;
    bool noValues = false;

    po::options_description argOpts("Options");
    argOpts.add_options()
        ("help,h", "Show help message.")
        ("path,p", po::value<string>(&pathRegex)->value_name("regex"),
         "Report only paths matching this regex.  For 'layer' and "
         "'pseudoLayer' output types, include all descendants of matching "
         "paths.")
        ("field,f", po::value<string>(&fieldRegex)->value_name("regex"),
         "Report only fields matching this regex.")
        ("time,t", po::value<vector<string>>(&timeSpecs)->
         multitoken()->value_name("n or ff..lf"),
         "Report only these times or time ranges for 'timeSamples' fields.")
        ("timeTolerance", po::value<double>(&timeTolerance)->
         default_value(timeTolerance)->value_name("tol"),
         "Report times that are close to those requested within this "
         "relative tolerance.")
        ("arraySizeLimit", po::value<int64_t>(&arraySizeLimit)->value_name("N"),
         "Truncate arrays with more than this many elements.  If -1, do not "
         "truncate arrays.  Default: 0 for 'outline' output, 8 for "
         "'pseudoLayer' output, and -1 for 'layer' output.")
        ("timeSamplesSizeLimit",
         po::value<int64_t>(&timeSamplesSizeLimit)->value_name("N"),
         "Truncate timeSamples with more than this many values.  If -1, do not "
         "truncate timeSamples.  Default: 0 for 'outline' output, 8 for "
         "'pseudoLayer' output, and -1 for 'layer' output.  Truncation "
         "performed after initial filtering by --time arguments.")
        ("out,o",
         po::value<string>(&outputFile)->default_value(std::string())->
         value_name("outputFile"),
         "Direct output to this file.  Use the "
         "'outputFormat' for finer control over the underlying format for "
         "output formats that are not uniquely determined by file extension.")
        ("outputType",
         po::value<OutputType>(&outputType)->default_value(outputType)->
         value_name("validity|summary|outline|pseudoLayer|layer"),
         "Specify output format; 'summary' reports overall statistics, "
         "'outline' is a flat text report of paths and fields, "
         "'pseudoLayer' is similar to the sdf file format but with truncated "
         "array values and timeSamples for human readability, and 'layer' is "
         "true layer output, with the format controlled by the 'outputFile' "
         "and 'outputFormat' arguments.")
        ("outputFormat",
         po::value<string>(&outputFormat)->value_name("format"),
         "Supply this as the 'format' entry of SdfFileFormatArguments for "
         "'layer' output to a file.  Requires both 'layer' output and a "
         "specified 'outputFile'.")
        ("sortBy", po::value<SortKey>(&sortKey)->default_value(sortKey)->
         value_name("path|field"),
         "Group 'outline' output by either path or field.  Ignored for other "
         "output types.")
        ("noValues", po::bool_switch(&noValues),
         "Do not report field values for 'outline' output.  Ignored for other "
         "output types.")
        ;

    po::options_description inputFile("Input");
    inputFile.add_options()
        ("input-file", po::value<vector<string>>(&inputFiles), "input files");

    po::options_description allOpts;
    allOpts.add(argOpts).add(inputFile);

    po::variables_map vm;
    try {
        po::positional_options_description p;
        p.add("input-file", -1);
        po::store(po::command_line_parser(argc, argv).
                  options(allOpts).positional(p).run(), vm);
        po::notify(vm);
        ParseTimes(timeSpecs, &literalTimes, &timeRanges);
    } catch (std::exception const &e) {
        ErrExit("%s", e.what());
    }

    if (vm.count("help") || inputFiles.empty()) {
        fprintf(stderr, "Usage: %s [options] <input files>\n",
                progName.c_str());
        fprintf(stderr, "%s\n", TfStringify(argOpts).c_str());
        exit(1);
    }

    std::shared_ptr<TfPatternMatcher> pathMatcher(
        pathRegex != ".*" ? new TfPatternMatcher(pathRegex) : nullptr);
    if (pathMatcher && !pathMatcher->IsValid()) {
        ErrExit("path regex '%s' : %s", pathRegex.c_str(),
                pathMatcher->GetInvalidReason().c_str());
    }

    std::shared_ptr<TfPatternMatcher> fieldMatcher(
        fieldRegex != ".*" ? new TfPatternMatcher(fieldRegex) : nullptr);
    if (fieldMatcher && !fieldMatcher->IsValid()) {
        ErrExit("field regex '%s' : %s", fieldRegex.c_str(),
                fieldMatcher->GetInvalidReason().c_str());
    }

    // If --out was specified, it must either not exist or must be writable.  If
    // the output type is 'layer', then the extension must correspond to a known
    // Sdf file format and we must have exactly one input file.  If the output
    // type is not 'layer', then the extension must not correspond to a known
    // Sdf file format.
    if (!outputFile.empty()) {
        if (TfIsFile(outputFile) && !TfIsWritable(outputFile)) {
            ErrExit("no write permission for existing output file '%s'",
                    outputFile.c_str());
        }
        // Using --out With 'layer' outputType there must be exactly one input
        // file, and the output file must have a known Sdf file format.
        if (outputType == OutputLayer) {
            if (inputFiles.size() > 1) {
                ErrExit("must supply exactly one input file with "
                        "'--outputType layer'");
            }
            if (!SdfFileFormat::FindByExtension(
                    TfStringGetSuffix(outputFile))) {
                ErrExit("no known Sdf file format for output file '%s'",
                        outputFile.c_str());
            }
        }
        // On the other hand, using --out with any other output type must not
        // correspond to an Sdf format.
        if (outputType != OutputLayer) {
            if (SdfFileFormat::FindByExtension(
                    TfStringGetSuffix(outputFile))) {
                ErrExit("output type '%s' does not produce content compatible "
                        "with the format for output file '%s'",
                        TfStringify(outputType).c_str(), outputFile.c_str());
            }
        }

        // Truncate the output file to start.
        FILE *f = fopen(outputFile.c_str(), "w");
        if (!f) {
            ErrExit("Failed to truncate output file '%s'", outputFile.c_str());
        }
        fclose(f);
    }

    // Set defaults for arraySizeLimit and timeSamplesSizeLimit.
    if (arraySizeLimit == -2 /* unset */) {
        arraySizeLimit =
            outputType == OutputPseudoLayer ? 8 :
            outputType == OutputLayer ? -1 : 0;
    }
    if (timeSamplesSizeLimit == -2 /* unset */) {
        timeSamplesSizeLimit =
            outputType == OutputPseudoLayer ? 8 :
            outputType == OutputLayer ? -1 : 0;
    }

    ReportParams params;
    params.pathMatcher = pathMatcher;
    params.fieldMatcher = fieldMatcher;

    params.outputType = outputType;
    params.outputFile = outputFile;
    params.outputFormat = outputFormat;

    params.literalTimes = literalTimes;
    params.timeRanges = timeRanges;
    params.timeTolerance = timeTolerance;

    params.sortKey = sortKey;
    params.arraySizeLimit = arraySizeLimit;
    params.timeSamplesSizeLimit = timeSamplesSizeLimit;
    params.showValues = !noValues;

    for (auto const &file: inputFiles) {
        TF_DESCRIBE_SCOPE("Opening layer @%s@", file.c_str());
        auto layer = SdfLayer::FindOrOpen(file);
        if (!layer) {
            Err("failed to open layer <%s>", file.c_str());
        }
        else {
            Process(layer, params);
        }
    }

    return 0;
}

max_stars_repo_name: octarrow/USD

max_issues_repo_name: octarrow/USD

max_forks_repo_name: octarrow/USD


text:
//
// Copyright 2007-2008 Christian Henning
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
#ifndef BOOST_GIL_IO_SCANLINE_READ_ITERATOR_HPP
#define BOOST_GIL_IO_SCANLINE_READ_ITERATOR_HPP

#include <boost/gil/io/error.hpp>
#include <boost/gil/io/typedefs.hpp>

#include <boost/iterator/iterator_facade.hpp>

#include <iterator>
#include <memory>
#include <vector>

namespace boost { namespace gil {

#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
#pragma warning(push)
#pragma warning(disable:4512) //assignment operator could not be generated
#endif

/// Input iterator to read images.
template <typename Reader>
class scanline_read_iterator
    : public boost::iterator_facade<scanline_read_iterator<Reader>, byte_t*, std::input_iterator_tag>
{
private:
    using base_t = boost::iterator_facade
        <
            scanline_read_iterator<Reader>,
            byte_t*,
            std::input_iterator_tag
        >;
public:
    scanline_read_iterator(Reader& reader, int pos = 0)
        : reader_(reader), pos_(pos)
    {
        buffer_       = std::make_shared<buffer_t>(buffer_t(reader_._scanline_length));
        buffer_start_ = &buffer_->front();
    }

private:
    friend class boost::iterator_core_access;

    void increment()
    {
        if (skip_scanline_)
        {
            reader_.skip(buffer_start_, pos_);
        }

        ++pos_;

        skip_scanline_ = true;
        read_scanline_ = true;
    }

    bool equal(scanline_read_iterator const& rhs) const
    {
        return pos_ == rhs.pos_;
    }

    typename base_t::reference dereference() const
    {
        if (read_scanline_)
        {
            reader_.read(buffer_start_, pos_);
        }
        skip_scanline_ = false;
        read_scanline_ = false;

        return buffer_start_;
    }

private:
    Reader& reader_;

    mutable int pos_            = 0;
    mutable bool read_scanline_ = true;
    mutable bool skip_scanline_ = true;

    using buffer_t     = std::vector<byte_t>;
    using buffer_ptr_t = std::shared_ptr<buffer_t>;
    buffer_ptr_t buffer_;
    mutable byte_t* buffer_start_ = nullptr;
};

#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
#pragma warning(pop)
#endif

} // namespace gil
} // namespace boost

#endif

ext: hpp


text:
#include "sudoku.h"

#include <muduo/base/Atomic.h>
#include <muduo/base/Logging.h>
#include <muduo/base/Thread.h>
#include <muduo/base/ThreadPool.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/TcpServer.h>
#include <muduo/net/inspect/Inspector.h>

#include <boost/bind.hpp>
#include <boost/circular_buffer.hpp>
#include <boost/noncopyable.hpp>

//#include <stdio.h>
//#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

#include "stat.h"

class SudokuServer : boost::noncopyable
{
 public:
  SudokuServer(EventLoop* loop,
               const InetAddress& listenAddr,
               int numEventLoops,
               int numThreads,
               bool nodelay)
    : server_(loop, listenAddr, "SudokuServer"),
      threadPool_(),
      numThreads_(numThreads),
      tcpNoDelay_(nodelay),
      startTime_(Timestamp::now()),
      stat_(threadPool_),
      inspectThread_(),
      inspector_(inspectThread_.startLoop(), InetAddress(9982), "sudoku-solver")
  {
    LOG_INFO << "Use " << numEventLoops << " IO threads.";
    LOG_INFO << "TCP no delay " << nodelay;

    server_.setConnectionCallback(
        boost::bind(&SudokuServer::onConnection, this, _1));
    server_.setMessageCallback(
        boost::bind(&SudokuServer::onMessage, this, _1, _2, _3));
    server_.setThreadNum(numEventLoops);

    inspector_.add("sudoku", "stats", boost::bind(&SudokuStat::report, &stat_),
                   "statistics of sudoku solver");
    inspector_.add("sudoku", "reset", boost::bind(&SudokuStat::reset, &stat_),
                   "reset statistics of sudoku solver");
  }

  void start()
  {
    LOG_INFO << "Starting " << numThreads_ << " computing threads.";
    threadPool_.start(numThreads_);
    server_.start();
  }

 private:
  void onConnection(const TcpConnectionPtr& conn)
  {
    LOG_TRACE << conn->peerAddress().toIpPort() << " -> "
        << conn->localAddress().toIpPort() << " is "
        << (conn->connected() ? "UP" : "DOWN");
    if (conn->connected())
    {
      if (tcpNoDelay_)
        conn->setTcpNoDelay(true);
      conn->setHighWaterMarkCallback(
          boost::bind(&SudokuServer::highWaterMark, this, _1, _2), 5 * 1024 * 1024);
      bool throttle = false;
      conn->setContext(throttle);
    }
  }

  void highWaterMark(const TcpConnectionPtr& conn, size_t tosend)
  {
    LOG_WARN << conn->name() << " high water mark " << tosend;
    if (tosend < 10 * 1024 * 1024)
    {
      conn->setHighWaterMarkCallback(
          boost::bind(&SudokuServer::highWaterMark, this, _1, _2), 10 * 1024 * 1024);
      conn->setWriteCompleteCallback(boost::bind(&SudokuServer::writeComplete, this, _1));
      bool throttle = true;
      conn->setContext(throttle);
    }
    else
    {
      conn->send("Bad Request!\r\n");
      conn->shutdown();  // FIXME: forceClose() ?
      stat_.recordBadRequest();
    }
  }

  void writeComplete(const TcpConnectionPtr& conn)
  {
    LOG_INFO << conn->name() << " write complete";
    conn->setHighWaterMarkCallback(
        boost::bind(&SudokuServer::highWaterMark, this, _1, _2), 5 * 1024 * 1024);
    conn->setWriteCompleteCallback(WriteCompleteCallback());
    bool throttle = false;
    conn->setContext(throttle);
  }

  void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)
  {
    size_t len = buf->readableBytes();
    while (len >= kCells + 2)
    {
      const char* crlf = buf->findCRLF();
      if (crlf)
      {
        string request(buf->peek(), crlf);
        buf->retrieveUntil(crlf + 2);
        len = buf->readableBytes();
        stat_.recordRequest();
        if (!processRequest(conn, request, receiveTime))
        {
          conn->send("Bad Request!\r\n");
          conn->shutdown();
          stat_.recordBadRequest();
          break;
        }
      }
      else if (len > 100) // id + ":" + kCells + "\r\n"
      {
        conn->send("Id too long!\r\n");
        conn->shutdown();
        stat_.recordBadRequest();
        break;
      }
      else
      {
        break;
      }
    }
  }

  struct Request
  {
    string id;
    string puzzle;
    Timestamp receiveTime;
  };

  bool processRequest(const TcpConnectionPtr& conn, const string& request, Timestamp receiveTime)
  {
    Request req;
    req.receiveTime = receiveTime;

    string::const_iterator colon = find(request.begin(), request.end(), ':');
    if (colon != request.end())
    {
      req.id.assign(request.begin(), colon);
      req.puzzle.assign(colon+1, request.end());
    }
    else
    {
      // when using thread pool, an id must be provided in the request.
      if (numThreads_ > 1)
        return false;
      req.puzzle = request;
    }

    if (req.puzzle.size() == implicit_cast<size_t>(kCells))
    {
      bool throttle = boost::any_cast<bool>(conn->getContext());
      if (threadPool_.queueSize() < 1000 * 1000 && !throttle)
      {
        threadPool_.run(boost::bind(&SudokuServer::solve, this, conn, req));
      }
      else
      {
        if (req.id.empty())
        {
          conn->send("ServerTooBusy\r\n");
        }
        else
        {
          conn->send(req.id + ":ServerTooBusy\r\n");
        }
        stat_.recordDroppedRequest();
      }
      return true;
    }
    return false;
  }

  void solve(const TcpConnectionPtr& conn, const Request& req)
  {
    LOG_DEBUG << conn->name();
    string result = solveSudoku(req.puzzle);
    if (req.id.empty())
    {
      conn->send(result + "\r\n");
    }
    else
    {
      conn->send(req.id + ":" + result + "\r\n");
    }
    stat_.recordResponse(Timestamp::now(), req.receiveTime, result != kNoSolution);
  }

  TcpServer server_;
  ThreadPool threadPool_;
  const int numThreads_;
  const bool tcpNoDelay_;
  const Timestamp startTime_;

  SudokuStat stat_;
  EventLoopThread inspectThread_;
  Inspector inspector_;
};

int main(int argc, char* argv[])
{
  LOG_INFO << argv[0] << " [number of IO threads] [number of worker threads] [-n]";
  LOG_INFO << "pid = " << getpid() << ", tid = " << CurrentThread::tid();
  int numEventLoops = 0;
  int numThreads = 0;
  bool nodelay = false;
  if (argc > 1)
  {
    numEventLoops = atoi(argv[1]);
  }
  if (argc > 2)
  {
    numThreads = atoi(argv[2]);
  }
  if (argc > 3 && string(argv[3]) == "-n")
  {
    nodelay = true;
  }

  EventLoop loop;
  InetAddress listenAddr(9981);
  SudokuServer server(&loop, listenAddr, numEventLoops, numThreads, nodelay);

  server.start();

  loop.loop();
}

ext: cc


text:
/* -*-C++-*-
   (c) Copyright 2008, Hewlett-Packard Development Company, LP

   See the file named COPYING for license details
*/

#ifndef LINTEL_PROGRAM_OPTIONS_HPP
#define LINTEL_PROGRAM_OPTIONS_HPP

#include <inttypes.h>

#include <iostream>
#include <utility>

#include <boost/bind.hpp>
#include <boost/program_options.hpp>

#include <Lintel/AssertBoost.hpp>

/** @file

    \brief Program options handling, wrapper around boost::program_options

    Program options handling; simplest usage is:

    @verbatim

    using namespace std;
    // specify default seed as time() ^ getpid()
    lintel::ProgramOption<int> seed("seed", "specify the random seed", time() ^ getpid());
    lintel::ProgramOption<bool> help("help", "get help");
    lintel::ProgramOption< vector<string> > params("param", "specify string parameters");

    int main(int argc, char *argv[]) {
        vector<string> files = lintel::parseCommandLine(argc, argv, true);
	if (files.empty() || help.get()) {
	    cout << boost::format("Usage: %s [--help] [--seed=#] [--param=parameter] file...")
	        % argv[0];
            exit(0);
        }
	random_number_generator.seed(seed.get());
	if (params.used()) {
	    BOOST_FOREACH(const string &param, params.get()) {
	        handleParamter(param);
            }
        }

	...;
    }

    Slightly more advanced usage would be to specify:

    void skewHandler(const boost::program_options::variable_value &opt) {
       if (opt.empty()) {
           ... do default handling ...
       } else {
           ... do handling with opt.as<T>() ...
       }
    }

    lintel::ProgramOption<double> skew("skew", "time skew to apply",
                                       boost::bind(&skewHandler, _1));

    Which will result in skewHandler being called when a call to
    lintel::parseCommandLine is made.

    @endverbatim
*/

namespace lintel {
    namespace detail {
	typedef boost::function<void (const boost::program_options::variable_value &)>
	  ProgramOptionFnT;
	typedef std::pair<std::string, ProgramOptionFnT> ProgramOptionPair;

	// TODO: Use the boost support for multiple option groups rather than what we do here.
	boost::program_options::options_description &programOptionsDesc(bool is_hidden = false);

	std::vector<ProgramOptionPair> &programOptionsActions();

        // Helper to "pretty" print the default value in description message.
        template<typename T> std::string defaultValueString(const T &def_val) {
            return str(boost::format("[%1%]") % def_val);
        }
        // overload general helper for char's since they default to value 0
        // but cast to string. This leads to ugly line breaks/missing text in
        // default value message.
        template<> inline std::string defaultValueString<char>(const char &def_val) {
            return str(boost::format("[\'\\%03d\']") % static_cast<int>(def_val));
        }
        // Helper to pretty print default values of vector program options
        template<typename T> std::string defaultValueString(const std::vector<T> &def_vector) {
            std::string vdesc = "[";
            for(uint32_t i = 0; i < def_vector.size(); ++i) {
                vdesc += defaultValueString(def_vector[i]);
            }
            vdesc += "]";
            return vdesc;
        }

        typedef boost::function<void ()> ProgramOptionInitFn;

        void addProgramOptionInitFn(const ProgramOptionInitFn &initfn);
        void runProgramOptionInitFns();
    }

    /// add string to help message, should be called before a call to
    /// parseCommandLine.  Will be printed after the 'Usage: argv[0]' bit.
    void programOptionsHelp(const std::string &to_add);

    /// Adds the string to the help message; since it is in the constructor it can be done as a
    /// global variable and hence happen before main().
    class OptionsHelpAdder {
    public:
        OptionsHelpAdder(const std::string &to_add) {
            programOptionsHelp(to_add);
        }
    };

    /// print out the usage information. Uses value set by setArgvZero or by
    /// parseCommandLine(argc,argv) for program name.
    void programOptionsUsage();

    /// print out the usage information. Uses argv0 passed in as program name.
    void programOptionsUsage(const std::string &argv0);

    /// sets program name for progamOptionsUsage
    void setArgvZero(const std::string &argv0);

    /// parse command line options as a standard argc, argv pair; sets the
    /// program name (argv[0]) for ProgramOptionsUsage via setArgvZero.
    ///
    /// @return any un-parsed arguments if allow_unrecognized is true
    std::vector<std::string> parseCommandLine(int argc, char *argv[],
					      bool allow_unrecognized = false);

    /// parse command line options as vector of strings
    ///
    /// @return any un-parsed arguments if allow_unrecognized is true
    std::vector<std::string> parseCommandLine(const std::vector<std::string> &args,
					      bool allow_unrecognized = false);

    /// \brief Class for constructing program arguments more easily.
    class ProgramArguments {
    public:
	ProgramArguments() { }

	ProgramArguments(const std::string &arg) {
	    args.push_back(arg);
	}

	ProgramArguments(const boost::format &f) {
	    args.push_back(f.str());
	}

	ProgramArguments &operator <<(const std::string &arg) {
	    args.push_back(arg);
	    return *this;
	}

	ProgramArguments &operator <<(const boost::format &f) {
	    args.push_back(f.str());
	    return *this;
	}

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

    inline void parseCommandLine(const ProgramArguments &pa) {
	parseCommandLine(pa.args);
    }

    inline void parseCommandLine(const std::string &arg) {
	parseCommandLine(ProgramArguments(arg));
    }

    /// \brief A single program option.
    ///
    /// Generic template program option, you can use ProgramOption<
    /// vector<int> > to allow for multiple values to be specified, or
    /// just ProgramOption<int> for one value.
    template<typename T> class ProgramOption {
    public:
	/// Program option without an action function; expected to be
	/// accessed via the used() and get() functions.  If you specify
	/// a default value, or the default initialization of T is a
	/// sane default, you can skip used().
	///
	/// @param name option name, will match with --name; should be a static string
	/// @param desc option description, will be printed when --help/-h is used;
	///             should be a static string
	/// @param in_default_val default value for the option
	ProgramOption(const std::string &name, const std::string &desc,
		      // TODO: This should be removed, and we should
		      // force people to specify a default Make a
		      // 2-arg and a 3 arg, and put
		      // FUNC_DEPRECATED_PREFIX before the 2 valued
		      // and FUNC_DEPRICATED after it, the fix a bunch of them.
		      //
		      // May want to check downstream packages if a
		      // similar todo is in there.
		      const T &in_default_val = T())
	    : default_val(in_default_val)
	{
	    init(name, desc);
	}

	bool used() const {
	    return !saved.empty();
	}

	T get() const {
	    if (used()) {
		return saved.as<T>();
	    } else {
		return default_val;
	    }
	}

	void set(const T &val) {
            saved = boost::program_options::variable_value(val, false);
	}

    protected:
	ProgramOption(const std::string &name, const std::string &desc,
		      const T &in_default_val, bool do_description)
	    : default_val(in_default_val)
	{
	    init(name, desc, do_description);
	}

    private:
	void init(const std::string &name, const std::string &desc,
		  bool do_help=true) {
            detail::addProgramOptionInitFn(boost::bind(&ProgramOption<T>::doInit, this,
                                                       name, desc, do_help));
	}

        void doInit(const std::string &name, const std::string &desc, bool do_help) {
	    std::string def_desc = str(boost::format("%s (Default value %s.)")
		    % desc % detail::defaultValueString(default_val));
	    detail::programOptionsDesc(!do_help).add_options()
		(name.c_str(), boost::program_options::value<T>(), def_desc.c_str());
	    detail::ProgramOptionFnT f = boost::bind(&ProgramOption<T>::save, this, _1);
	    detail::programOptionsActions().push_back(std::make_pair(name, f));
        }

	void save(const boost::program_options::variable_value &opt) {
	    if (!opt.empty()) { // Do not overwrite saved opts with empty values.
		saved = opt;
	    }
	}

	boost::program_options::variable_value saved;
	const T default_val;
    };

    /// \brief Program option which won't appear in the usage unless in debug mode
    template<typename T> class TestingOption : public ProgramOption<T> {
    public:
#if LINTEL_DEBUG
	static const bool show_description = true;
#else
	static const bool show_description = false;
#endif

	TestingOption(const std::string &name, const std::string &desc,
		      const T &in_default_val = T())
	    : ProgramOption<T>(name, desc, in_default_val, show_description) { }
    };

    /// \brief Special case boolean program option
    ///
    /// Special case for program options without values, e.g.,
    /// "--help", which doesn't carry a value like "--seed=100"
    template<> class ProgramOption<bool> {
    public:
	ProgramOption(const std::string &name, const std::string &desc)
	    : val(false)
	{
	    init(name, desc);
	}

	bool used() const {
	    return val;
	}

	bool get() const {
	    return val;
	}

	void set(const bool &_val) {
            val = _val;
	}

    protected:
	ProgramOption(const std::string &name, const std::string &desc, bool do_description)
	    : val(false)
	{
	    init(name, desc, do_description);
	}

    private:
	void init(const std::string &name, const std::string &desc,
		  bool do_help=true) {
            detail::addProgramOptionInitFn(boost::bind(&ProgramOption<bool>::doInit, this,
                                                       name, desc, do_help));
        }

        void doInit(const std::string &name, const std::string &desc, bool do_help) {
	    detail::programOptionsDesc(!do_help).add_options() (name.c_str(), desc.c_str());
	    detail::ProgramOptionFnT f = boost::bind(&ProgramOption<bool>::save, this, _1);
	    detail::programOptionsActions().push_back(std::make_pair(name, f));
	}

	void save(const boost::program_options::variable_value &opt) {
	    if (!opt.empty()) {
		val = true;
	    }
	}

	bool val;
    };

    /// \brief Hidden option which also doesn't take an argument
    template<> class TestingOption<bool> : public ProgramOption<bool> {
    public:
	static const bool show_description = TestingOption<int>::show_description;

	TestingOption(const std::string &name, const std::string &desc)
	    : ProgramOption<bool>(name, desc, show_description) { }
    };

    void processOptions(std::vector<detail::ProgramOptionPair> &actions,
			boost::program_options::variables_map &var_map);

    void parseConfigFile(const std::string &name);

    template<class charT>
    void parseConfigFile(std::basic_istream<charT> &t) {
        detail::runProgramOptionInitFns();
        boost::program_options::variables_map var_map;
        boost::program_options::store(boost::program_options::parse_config_file
                                      (t, detail::programOptionsDesc()), var_map);
        boost::program_options::notify(var_map);
        processOptions(detail::programOptionsActions(), var_map);
    }
}

#endif

max_stars_repo_name: sbu-fsl/Lintel

max_issues_repo_name: sbu-fsl/Lintel

max_forks_repo_name: sbu-fsl/Lintel


text:
#ifndef DBL_IPC_IPC_HXX
#define DBL_IPC_IPC_HXX

#include "dbl/core/common.hxx"

#include <string>
#include <set>
#include <memory>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/offset_ptr.hpp>

namespace dbl {
namespace ipc {

namespace bipc = boost::interprocess;

template <class T>
class SharedMemory
{
public:
	SharedMemory() = delete;
	SharedMemory(const std::string& name, std::size_t size = 65535);
	virtual ~SharedMemory();
	T* get_object();
private:
	const std::string name_;
	std::unique_ptr<bipc::managed_shared_memory> segment_;
	bipc::offset_ptr<T> object_ptr_;
};

template <class T>
SharedMemory<T>::SharedMemory(const std::string& name, std::size_t size)
	: name_(name)
{
	segment_.reset(
		new bipc::managed_shared_memory(
			bipc::open_or_create,
			name_.c_str(),
			size
		)
	);
	object_ptr_ = static_cast<T*>(segment_->allocate(sizeof(T)));
}

template <class T>
SharedMemory<T>::~SharedMemory()
{
	LOG(DEBUG) << "Deallocating shared memory";
	segment_->deallocate(object_ptr_.get());
	LOG(DEBUG) << "Removing shared memory object";
	bipc::shared_memory_object::remove(name_.c_str());
}

template <class T>
T* SharedMemory<T>::get_object()
{
	return object_ptr_.get();
}

} // ipc
} // dbl

#endif

ext: hxx

max_stars_repo_name: mcptr/dbl-service

max_issues_repo_name: mcptr/dbl-service

max_forks_repo_name: mcptr/dbl-service