我们有一个可帮助我们将数据发送到telegraf / influxdb的类(即用于监视)。大致如下:
class TelegrafSend {
public:
// // Constructor does some default stuff based on binary name and context.
TelegrafSend();
// Destructor sends the object.
~TelegrafSend();
// Exists in a couple variants. Probably could have been a template.
void AddTag(const std::string& tag_name, const std::string& tag_value);
// Same.
void AddField(const std::string& field_name, const int field_value);
};
要清楚,它看起来像这样:
TelegrafSend TelegrafSend::AddField(const string& field_name, const int field_value) {
fields_[field_name] = to_string(field_value);
sent_ = false;
return *this;
}
效果很好:
TelegrafSend telegraf;
telegraf.AddTag("a_tag", "a_value");
telegraf.AddField(kTelegrafCount, 1);
并且当它超出范围时,它会被发送,这是很好的行为,因为函数可以在执行时添加几个度量,并且该函数的所有退出都会导致对象发送。
现在我有了一个聪明的主意:
class TelegrafSend {
public:
// // Constructor does some default stuff based on binary name and context.
TelegrafSend();
// Destructor sends the object.
~TelegrafSend();
// Exists in a couple variants. Probably could have been a template.
TelegrafSend AddTag(const std::string& tag_name, const std::string& tag_value);
// Same.
TelegrafSend AddField(const std::string& field_name, const int field_value);
};
所以我可以写
TelegrafSend telegraf.AddTag("a_tag", "a_value").AddField(kTelegrafCount, 1);
这里的问题是我正在创建临时文件,因此尽管最终可以使用,但每次返回都会创建一个临时文件,该临时文件将被销毁并发送到Telegraf。对于influxdb而言,这实际上是低效的,甚至没有谈论C ++中的不良做法。
我已经尝试了一些关于返回右值引用的变体,但是要么尝试返回对临时变量或堆栈变量的引用,要么做一些同样愚蠢的事情。我在生产中发现的示例还有很多其他方面,我不确定该怎么做。
是否有针对这种模式的最佳实践的指针?还是我正在尝试做一些我不应该做的语法操作?
答案 0 :(得分:2)
您必须在这些方法中返回对self的引用,而不是创建新对象。
也可以考虑实现move构造函数。
class TelegrafSend {
public:
TelegrafSend();
~TelegrafSend();
TelegrafSend(const TelegrafSend&) = delete;
TelegrafSend& operator = (const TelegrafSend&) = delete;
TelegrafSend(TelegrafSend&&); // possibly = delete;
TelegrafSend& operator = (TelegrafSend&&); // possibly = delete;
// Exists in a couple variants. Probably could have been a template.
TelegrafSend& AddTag(const std::string& tag_name, const std::string& tag_value)
{
/*..*/
return *this;
}
// Same.
TelegrafSend& AddField(const std::string& field_name, const int field_value)
{
/*..*/
return *this;
}
};
然后您可以使用:
TelegrafSend{}.AddTag("a_tag", "a_value").AddField(kTelegrafCount, 1);
答案 1 :(得分:0)
我将根据std::optional
实现并提供一个move构造函数,以便在析构函数期间提供自动的“有效性”指示符。
请注意,从可选选项移走不会清空它,只会将内容移出,因此您必须重置该可选项。
完整示例:
#include <optional>
#include <string>
#include <iostream>
struct TelegrafSend
{
// // Constructor does some default stuff based on binary name and context.
TelegrafSend();
TelegrafSend(TelegrafSend&& other);
TelegrafSend(TelegrafSend const& ) = delete;
TelegrafSend& operator=(TelegrafSend const& ) = delete;
TelegrafSend& operator=(TelegrafSend && ) = delete;
// Destructor sends the object.
~TelegrafSend();
TelegrafSend& AddTag(const std::string& tag_name, const std::string& tag_value);
TelegrafSend& AddField(const std::string& field_name, const int field_value);
private:
struct Impl
{
std::string narrative;
void more(std::string const& s)
{
if (!narrative.empty())
narrative += '\n';
narrative += s;
}
void emit()
{
if (narrative.empty())
std::cout << "{empty}\n";
else
std::cout << narrative << '\n';
}
};
std::optional<Impl> impl_;
};
TelegrafSend::TelegrafSend()
: impl_(Impl())
{
}
TelegrafSend::TelegrafSend(TelegrafSend&& other)
: impl_(std::move(other.impl_))
{
other.impl_.reset();
}
TelegrafSend::~TelegrafSend()
{
if(impl_.has_value())
impl_->emit();
}
TelegrafSend& TelegrafSend::AddTag(const std::string& tag_name, const std::string& tag_value)
{
auto s = "Tag : " + tag_name + " : " + tag_value;
impl_->more(s);
return *this;
}
TelegrafSend& TelegrafSend::AddField(const std::string& field_name, const int field_value)
{
auto s = "Field : " + field_name + " : " + std::to_string(field_value);
impl_->more(s);
return *this;
}
auto test(TelegrafSend ts = {}) -> TelegrafSend
{
ts.AddTag("foo", "bar").AddField("baz", 6);
return ts;
}
int main()
{
{
test(), std::cout << "hello\n";
}
std::cout << "world\n";
}
预期输出:
hello
Tag : foo : bar
Field : baz : 6
world