mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-29 02:51:57 +00:00
Speed up build
This commit is contained in:
parent
c68548a2ca
commit
bee8bacefd
@ -5,6 +5,7 @@
|
|||||||
#include "optional.h"
|
#include "optional.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct FileContents {
|
struct FileContents {
|
||||||
|
@ -30,6 +30,15 @@ void VerifyUnique(const std::vector<T>& values0) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void RemoveRange(std::vector<T>* dest, const std::vector<T>& to_remove) {
|
||||||
|
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
|
||||||
|
dest->erase(
|
||||||
|
std::remove_if(dest->begin(), dest->end(),
|
||||||
|
[&](const T& t) { return to_remove_set.count(t) > 0; }),
|
||||||
|
dest->end());
|
||||||
|
}
|
||||||
|
|
||||||
optional<QueryType::Def> ToQuery(const IdMap& id_map,
|
optional<QueryType::Def> ToQuery(const IdMap& id_map,
|
||||||
const IndexType::Def& type) {
|
const IndexType::Def& type) {
|
||||||
if (type.detailed_name.empty())
|
if (type.detailed_name.empty())
|
||||||
|
204
src/test.cc
204
src/test.cc
@ -5,8 +5,12 @@
|
|||||||
#include "serializer.h"
|
#include "serializer.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <doctest/doctest.h>
|
||||||
|
#include <loguru/loguru.hpp>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
// The 'diff' utility is available and we can use dprintf(3).
|
// The 'diff' utility is available and we can use dprintf(3).
|
||||||
@ -33,6 +37,155 @@ std::string ToString(const rapidjson::Document& document) {
|
|||||||
return UpdateToRnNewlines(output);
|
return UpdateToRnNewlines(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParseTestExpectation(
|
||||||
|
const std::string& filename,
|
||||||
|
const std::vector<std::string>& lines_with_endings,
|
||||||
|
TextReplacer* replacer,
|
||||||
|
std::vector<std::string>* flags,
|
||||||
|
std::unordered_map<std::string, std::string>* output_sections) {
|
||||||
|
#if false
|
||||||
|
#include "bar.h"
|
||||||
|
|
||||||
|
void foo();
|
||||||
|
|
||||||
|
/*
|
||||||
|
// DOCS for TEXT_REPLACE:
|
||||||
|
// Each line under TEXT_REPLACE is a replacement, ie, the two entries will be
|
||||||
|
// considered equivalent. This is useful for USRs which vary across files.
|
||||||
|
|
||||||
|
// DOCS for EXTRA_FLAGS:
|
||||||
|
// Additional flags to pass to clang.
|
||||||
|
|
||||||
|
// DOCS for OUTPUT:
|
||||||
|
// If no name is given assume to be this file name. If there is not an output
|
||||||
|
// section for a file it is not checked.
|
||||||
|
|
||||||
|
TEXT_REPLACE:
|
||||||
|
foo <===> bar
|
||||||
|
one <===> two
|
||||||
|
|
||||||
|
EXTRA_FLAGS:
|
||||||
|
-std=c++14
|
||||||
|
|
||||||
|
OUTPUT:
|
||||||
|
{}
|
||||||
|
|
||||||
|
OUTPUT: bar.cc
|
||||||
|
{}
|
||||||
|
|
||||||
|
OUTPUT: bar.h
|
||||||
|
{}
|
||||||
|
*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Scan for TEXT_REPLACE:
|
||||||
|
{
|
||||||
|
bool in_output = false;
|
||||||
|
for (std::string line : lines_with_endings) {
|
||||||
|
TrimInPlace(line);
|
||||||
|
|
||||||
|
if (StartsWith(line, "TEXT_REPLACE:")) {
|
||||||
|
assert(!in_output && "multiple TEXT_REPLACE sections");
|
||||||
|
in_output = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_output && line.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (in_output) {
|
||||||
|
static const std::string kKey = " <===> ";
|
||||||
|
size_t index = line.find(kKey);
|
||||||
|
LOG_IF_S(FATAL, index == std::string::npos)
|
||||||
|
<< " No '" << kKey << "' in replacement string '" << line << "'"
|
||||||
|
<< ", index=" << index;
|
||||||
|
|
||||||
|
TextReplacer::Replacement replacement;
|
||||||
|
replacement.from = line.substr(0, index);
|
||||||
|
replacement.to = line.substr(index + kKey.size());
|
||||||
|
TrimInPlace(replacement.from);
|
||||||
|
TrimInPlace(replacement.to);
|
||||||
|
replacer->replacements.push_back(replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan for EXTRA_FLAGS:
|
||||||
|
{
|
||||||
|
bool in_output = false;
|
||||||
|
for (std::string line : lines_with_endings) {
|
||||||
|
TrimInPlace(line);
|
||||||
|
|
||||||
|
if (StartsWith(line, "EXTRA_FLAGS:")) {
|
||||||
|
assert(!in_output && "multiple EXTRA_FLAGS sections");
|
||||||
|
in_output = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_output && line.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (in_output)
|
||||||
|
flags->push_back(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan for OUTPUT:
|
||||||
|
{
|
||||||
|
std::string active_output_filename;
|
||||||
|
std::string active_output_contents;
|
||||||
|
|
||||||
|
bool in_output = false;
|
||||||
|
for (std::string line_with_ending : lines_with_endings) {
|
||||||
|
if (StartsWith(line_with_ending, "*/"))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (StartsWith(line_with_ending, "OUTPUT:")) {
|
||||||
|
// Terminate the previous output section if we found a new one.
|
||||||
|
if (in_output) {
|
||||||
|
(*output_sections)[active_output_filename] = active_output_contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to tokenize OUTPUT: based one whitespace. If there is more than
|
||||||
|
// one token assume it is a filename.
|
||||||
|
std::vector<std::string> tokens = SplitString(line_with_ending, " ");
|
||||||
|
if (tokens.size() > 1) {
|
||||||
|
active_output_filename = tokens[1];
|
||||||
|
TrimInPlace(active_output_filename);
|
||||||
|
} else {
|
||||||
|
active_output_filename = filename;
|
||||||
|
}
|
||||||
|
active_output_contents = "";
|
||||||
|
|
||||||
|
in_output = true;
|
||||||
|
} else if (in_output)
|
||||||
|
active_output_contents += line_with_ending;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_output)
|
||||||
|
(*output_sections)[active_output_filename] = active_output_contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateTestExpectation(const std::string& filename,
|
||||||
|
const std::string& expectation,
|
||||||
|
const std::string& actual) {
|
||||||
|
// Read the entire file into a string.
|
||||||
|
std::ifstream in(filename);
|
||||||
|
std::string str;
|
||||||
|
str.assign(std::istreambuf_iterator<char>(in),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
// Replace expectation
|
||||||
|
auto it = str.find(expectation);
|
||||||
|
assert(it != std::string::npos);
|
||||||
|
str.replace(it, expectation.size(), actual);
|
||||||
|
|
||||||
|
// Write it back out.
|
||||||
|
WriteToFile(filename, str);
|
||||||
|
}
|
||||||
|
|
||||||
void DiffDocuments(std::string path,
|
void DiffDocuments(std::string path,
|
||||||
std::string path_section,
|
std::string path_section,
|
||||||
rapidjson::Document& expected,
|
rapidjson::Document& expected,
|
||||||
@ -309,3 +462,54 @@ bool RunIndexTests(const std::string& filter_path, bool enable_update) {
|
|||||||
// TODO: ctor/dtor, copy ctor
|
// TODO: ctor/dtor, copy ctor
|
||||||
// TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile&
|
// TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile&
|
||||||
// refs.
|
// refs.
|
||||||
|
|
||||||
|
TEST_SUITE("ParseTestExpectation") {
|
||||||
|
TEST_CASE("Parse TEXT_REPLACE") {
|
||||||
|
// clang-format off
|
||||||
|
std::vector<std::string> lines_with_endings = {
|
||||||
|
"/*\n",
|
||||||
|
"TEXT_REPLACE:\n",
|
||||||
|
" foo <===> \tbar \n",
|
||||||
|
"01 <===> 2\n",
|
||||||
|
"\n",
|
||||||
|
"*/\n"};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
TextReplacer text_replacer;
|
||||||
|
std::vector<std::string> flags;
|
||||||
|
std::unordered_map<std::string, std::string> all_expected_output;
|
||||||
|
ParseTestExpectation("foo.cc", lines_with_endings, &text_replacer, &flags,
|
||||||
|
&all_expected_output);
|
||||||
|
|
||||||
|
REQUIRE(text_replacer.replacements.size() == 2);
|
||||||
|
REQUIRE(text_replacer.replacements[0].from == "foo");
|
||||||
|
REQUIRE(text_replacer.replacements[0].to == "bar");
|
||||||
|
REQUIRE(text_replacer.replacements[1].from == "01");
|
||||||
|
REQUIRE(text_replacer.replacements[1].to == "2");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Apply TEXT_REPLACE") {
|
||||||
|
TextReplacer replacer;
|
||||||
|
replacer.replacements.push_back(TextReplacer::Replacement{"foo", "bar"});
|
||||||
|
replacer.replacements.push_back(TextReplacer::Replacement{"01", "2"});
|
||||||
|
replacer.replacements.push_back(TextReplacer::Replacement{"3", "456"});
|
||||||
|
|
||||||
|
// Equal-length.
|
||||||
|
REQUIRE(replacer.Apply("foo") == "bar");
|
||||||
|
REQUIRE(replacer.Apply("bar") == "bar");
|
||||||
|
|
||||||
|
// Shorter replacement.
|
||||||
|
REQUIRE(replacer.Apply("01") == "2");
|
||||||
|
REQUIRE(replacer.Apply("2") == "2");
|
||||||
|
|
||||||
|
// Longer replacement.
|
||||||
|
REQUIRE(replacer.Apply("3") == "456");
|
||||||
|
REQUIRE(replacer.Apply("456") == "456");
|
||||||
|
|
||||||
|
// Content before-after replacement.
|
||||||
|
REQUIRE(replacer.Apply("aaaa01bbbb") == "aaaa2bbbb");
|
||||||
|
|
||||||
|
// Multiple replacements.
|
||||||
|
REQUIRE(replacer.Apply("foofoobar0123") == "barbarbar22456");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
200
src/utils.cc
200
src/utils.cc
@ -374,155 +374,6 @@ std::string TextReplacer::Apply(const std::string& content) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParseTestExpectation(
|
|
||||||
const std::string& filename,
|
|
||||||
const std::vector<std::string>& lines_with_endings,
|
|
||||||
TextReplacer* replacer,
|
|
||||||
std::vector<std::string>* flags,
|
|
||||||
std::unordered_map<std::string, std::string>* output_sections) {
|
|
||||||
#if false
|
|
||||||
#include "bar.h"
|
|
||||||
|
|
||||||
void foo();
|
|
||||||
|
|
||||||
/*
|
|
||||||
// DOCS for TEXT_REPLACE:
|
|
||||||
// Each line under TEXT_REPLACE is a replacement, ie, the two entries will be
|
|
||||||
// considered equivalent. This is useful for USRs which vary across files.
|
|
||||||
|
|
||||||
// DOCS for EXTRA_FLAGS:
|
|
||||||
// Additional flags to pass to clang.
|
|
||||||
|
|
||||||
// DOCS for OUTPUT:
|
|
||||||
// If no name is given assume to be this file name. If there is not an output
|
|
||||||
// section for a file it is not checked.
|
|
||||||
|
|
||||||
TEXT_REPLACE:
|
|
||||||
foo <===> bar
|
|
||||||
one <===> two
|
|
||||||
|
|
||||||
EXTRA_FLAGS:
|
|
||||||
-std=c++14
|
|
||||||
|
|
||||||
OUTPUT:
|
|
||||||
{}
|
|
||||||
|
|
||||||
OUTPUT: bar.cc
|
|
||||||
{}
|
|
||||||
|
|
||||||
OUTPUT: bar.h
|
|
||||||
{}
|
|
||||||
*/
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Scan for TEXT_REPLACE:
|
|
||||||
{
|
|
||||||
bool in_output = false;
|
|
||||||
for (std::string line : lines_with_endings) {
|
|
||||||
TrimInPlace(line);
|
|
||||||
|
|
||||||
if (StartsWith(line, "TEXT_REPLACE:")) {
|
|
||||||
assert(!in_output && "multiple TEXT_REPLACE sections");
|
|
||||||
in_output = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_output && line.empty())
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (in_output) {
|
|
||||||
static const std::string kKey = " <===> ";
|
|
||||||
size_t index = line.find(kKey);
|
|
||||||
LOG_IF_S(FATAL, index == std::string::npos)
|
|
||||||
<< " No '" << kKey << "' in replacement string '" << line << "'"
|
|
||||||
<< ", index=" << index;
|
|
||||||
|
|
||||||
TextReplacer::Replacement replacement;
|
|
||||||
replacement.from = line.substr(0, index);
|
|
||||||
replacement.to = line.substr(index + kKey.size());
|
|
||||||
TrimInPlace(replacement.from);
|
|
||||||
TrimInPlace(replacement.to);
|
|
||||||
replacer->replacements.push_back(replacement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan for EXTRA_FLAGS:
|
|
||||||
{
|
|
||||||
bool in_output = false;
|
|
||||||
for (std::string line : lines_with_endings) {
|
|
||||||
TrimInPlace(line);
|
|
||||||
|
|
||||||
if (StartsWith(line, "EXTRA_FLAGS:")) {
|
|
||||||
assert(!in_output && "multiple EXTRA_FLAGS sections");
|
|
||||||
in_output = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_output && line.empty())
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (in_output)
|
|
||||||
flags->push_back(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan for OUTPUT:
|
|
||||||
{
|
|
||||||
std::string active_output_filename;
|
|
||||||
std::string active_output_contents;
|
|
||||||
|
|
||||||
bool in_output = false;
|
|
||||||
for (std::string line_with_ending : lines_with_endings) {
|
|
||||||
if (StartsWith(line_with_ending, "*/"))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (StartsWith(line_with_ending, "OUTPUT:")) {
|
|
||||||
// Terminate the previous output section if we found a new one.
|
|
||||||
if (in_output) {
|
|
||||||
(*output_sections)[active_output_filename] = active_output_contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to tokenize OUTPUT: based one whitespace. If there is more than
|
|
||||||
// one token assume it is a filename.
|
|
||||||
std::vector<std::string> tokens = SplitString(line_with_ending, " ");
|
|
||||||
if (tokens.size() > 1) {
|
|
||||||
active_output_filename = tokens[1];
|
|
||||||
TrimInPlace(active_output_filename);
|
|
||||||
} else {
|
|
||||||
active_output_filename = filename;
|
|
||||||
}
|
|
||||||
active_output_contents = "";
|
|
||||||
|
|
||||||
in_output = true;
|
|
||||||
} else if (in_output)
|
|
||||||
active_output_contents += line_with_ending;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_output)
|
|
||||||
(*output_sections)[active_output_filename] = active_output_contents;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateTestExpectation(const std::string& filename,
|
|
||||||
const std::string& expectation,
|
|
||||||
const std::string& actual) {
|
|
||||||
// Read the entire file into a string.
|
|
||||||
std::ifstream in(filename);
|
|
||||||
std::string str;
|
|
||||||
str.assign(std::istreambuf_iterator<char>(in),
|
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
in.close();
|
|
||||||
|
|
||||||
// Replace expectation
|
|
||||||
auto it = str.find(expectation);
|
|
||||||
assert(it != std::string::npos);
|
|
||||||
str.replace(it, expectation.size(), actual);
|
|
||||||
|
|
||||||
// Write it back out.
|
|
||||||
WriteToFile(filename, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteToFile(const std::string& filename, const std::string& content) {
|
void WriteToFile(const std::string& filename, const std::string& content) {
|
||||||
std::ofstream file(filename,
|
std::ofstream file(filename,
|
||||||
std::ios::out | std::ios::trunc | std::ios::binary);
|
std::ios::out | std::ios::trunc | std::ios::binary);
|
||||||
@ -601,57 +452,6 @@ std::string UpdateToRnNewlines(std::string output) {
|
|||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_SUITE("ParseTestExpectation") {
|
|
||||||
TEST_CASE("Parse TEXT_REPLACE") {
|
|
||||||
// clang-format off
|
|
||||||
std::vector<std::string> lines_with_endings = {
|
|
||||||
"/*\n",
|
|
||||||
"TEXT_REPLACE:\n",
|
|
||||||
" foo <===> \tbar \n",
|
|
||||||
"01 <===> 2\n",
|
|
||||||
"\n",
|
|
||||||
"*/\n"};
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
TextReplacer text_replacer;
|
|
||||||
std::vector<std::string> flags;
|
|
||||||
std::unordered_map<std::string, std::string> all_expected_output;
|
|
||||||
ParseTestExpectation("foo.cc", lines_with_endings, &text_replacer, &flags,
|
|
||||||
&all_expected_output);
|
|
||||||
|
|
||||||
REQUIRE(text_replacer.replacements.size() == 2);
|
|
||||||
REQUIRE(text_replacer.replacements[0].from == "foo");
|
|
||||||
REQUIRE(text_replacer.replacements[0].to == "bar");
|
|
||||||
REQUIRE(text_replacer.replacements[1].from == "01");
|
|
||||||
REQUIRE(text_replacer.replacements[1].to == "2");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Apply TEXT_REPLACE") {
|
|
||||||
TextReplacer replacer;
|
|
||||||
replacer.replacements.push_back(TextReplacer::Replacement{"foo", "bar"});
|
|
||||||
replacer.replacements.push_back(TextReplacer::Replacement{"01", "2"});
|
|
||||||
replacer.replacements.push_back(TextReplacer::Replacement{"3", "456"});
|
|
||||||
|
|
||||||
// Equal-length.
|
|
||||||
REQUIRE(replacer.Apply("foo") == "bar");
|
|
||||||
REQUIRE(replacer.Apply("bar") == "bar");
|
|
||||||
|
|
||||||
// Shorter replacement.
|
|
||||||
REQUIRE(replacer.Apply("01") == "2");
|
|
||||||
REQUIRE(replacer.Apply("2") == "2");
|
|
||||||
|
|
||||||
// Longer replacement.
|
|
||||||
REQUIRE(replacer.Apply("3") == "456");
|
|
||||||
REQUIRE(replacer.Apply("456") == "456");
|
|
||||||
|
|
||||||
// Content before-after replacement.
|
|
||||||
REQUIRE(replacer.Apply("aaaa01bbbb") == "aaaa2bbbb");
|
|
||||||
|
|
||||||
// Multiple replacements.
|
|
||||||
REQUIRE(replacer.Apply("foofoobar0123") == "barbarbar22456");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_SUITE("Update \\n to \\r\\n") {
|
TEST_SUITE("Update \\n to \\r\\n") {
|
||||||
TEST_CASE("all") {
|
TEST_CASE("all") {
|
||||||
REQUIRE(UpdateToRnNewlines("\n") == "\r\n");
|
REQUIRE(UpdateToRnNewlines("\n") == "\r\n");
|
||||||
|
22
src/utils.h
22
src/utils.h
@ -7,8 +7,6 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// Trim from start (in place)
|
// Trim from start (in place)
|
||||||
@ -111,17 +109,6 @@ struct TextReplacer {
|
|||||||
std::string Apply(const std::string& content);
|
std::string Apply(const std::string& content);
|
||||||
};
|
};
|
||||||
|
|
||||||
void ParseTestExpectation(
|
|
||||||
const std::string& filename,
|
|
||||||
const std::vector<std::string>& lines_with_endings,
|
|
||||||
TextReplacer* text_replacer,
|
|
||||||
std::vector<std::string>* flags,
|
|
||||||
std::unordered_map<std::string, std::string>* output_sections);
|
|
||||||
|
|
||||||
void UpdateTestExpectation(const std::string& filename,
|
|
||||||
const std::string& expectation,
|
|
||||||
const std::string& actual);
|
|
||||||
|
|
||||||
void WriteToFile(const std::string& filename, const std::string& content);
|
void WriteToFile(const std::string& filename, const std::string& content);
|
||||||
|
|
||||||
// note: this implementation does not disable this overload for array types
|
// note: this implementation does not disable this overload for array types
|
||||||
@ -143,15 +130,6 @@ void AddRange(std::vector<T>* dest, std::vector<T>&& to_add) {
|
|||||||
std::make_move_iterator(to_add.end()));
|
std::make_move_iterator(to_add.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void RemoveRange(std::vector<T>* dest, const std::vector<T>& to_remove) {
|
|
||||||
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
|
|
||||||
dest->erase(
|
|
||||||
std::remove_if(dest->begin(), dest->end(),
|
|
||||||
[&](const T& t) { return to_remove_set.count(t) > 0; }),
|
|
||||||
dest->end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://stackoverflow.com/a/38140932
|
// http://stackoverflow.com/a/38140932
|
||||||
//
|
//
|
||||||
// struct SomeHashKey {
|
// struct SomeHashKey {
|
||||||
|
Loading…
Reference in New Issue
Block a user