Skip to content
Josh Blum edited this page Dec 12, 2017 · 2 revisions

Release 0.6 Notes

In an effort to better support coding with move semantics in C++11 and beyond, some of the port API calls have been extended to support operations like emplacements and rvalue references. These are non-breaking API additions designed to better reflect the capabilities of the language and the intentions of the user.

Using moves and emplacement usually implies a performance improvement by reducing copying of structures and reference counting overhead. Note that nothing here is not going to lead to a drastic improvement in overall performance as the work performed during a block's work cycle should probably exceed any performance gains by an order of magnitude. Use the new APIs when it makes sense semantically with the rest of the code. Harming readability of code to save a few copies is probably not worth it.

Much like the "emplace" call on STL containers like vector and map, labels be constructed into their destination container by forwarding all constructor arguments into a argument pack. Sounds complicated? No worries, this is strait forward:

Old syntax (still supported):

outPort->postLabel(Pothos::Label(id, data, index));

New emplacement syntax:

outPort->postLabel(id, data, index);

Any arguments accepted by Pothos::Label can be used, including optional arguments like width.

Also like STL containers, labels can be moved into the output port. Usually, the act of creating or modifying a label facilitates the creation a temporary label in which to manipulate. This label would normally be destructed on scope exit after pushing, so its preferable to move the label, which skips the extra copy step.

Example moving a label:

//copy the label into a mutable label
auto newLabel = someConstLabel;

//make changes to the new label
newLabel.index += offset;

//move the new label to the output port
outPort->postLabel(std::move(newLabel));

//new label should be considered invalid here

For blocks that operate directly on the BufferChunk objects. Buffers can now be taken from an input port, and moved to an output port.

Old syntax (still supported):

//grab the buffer from the input port
auto buffer = inputPort->buffer();

//length based on processing labels, etc...
buffer.length = newLength;

//post the buffer to the output port
outputPort->postBuffer(buffer);

//tell the input port how much was consumed
inputPort->consume(buffer.length);

New move syntax:

//move the buffer from the input port
//the input port no longer holds a reference
auto buffer = inputPort->takeBuffer();

//length based on processing labels, etc...
buffer.length = newLength;

//tell the input port how much was consumed
inputPort->consume(buffer.length);

//move the buffer to the output port
outputPort->postBuffer(std::move(buffer));

//buffer should be considered invalid here
  • Note0: Actually copying a BufferChunk is not a big deal;

A BufferChunk merely holds a reference to a buffer, along with address offset and length. So copying a BufferChunk is a not a memory copy, its just copying few numbers and incrementing a reference count.

  • Note1: Notice how consume uses the buffer's length?

In the second example, consume must come before the move because the buffer's length will not be valid after the move. However, if the number of bytes to consume was saved in another variable, consume itself can be in any order.

Messages can be created directly into the object container from constructor arguments. This means that the message doesn't even need to be moved to the destination container since it can be constructed directly into the message consumed by downstream blocks.

Emplacing a message:

//pass the type as the first template argument and then
//any number of arguments supported by the type's constructor
outputPort->emplaceMessage<std::string>(buffer, length);

Message moving was already supported, but many of the core blocks were not using move when applicable. Here is a quick example to demonstrate the concept:

Moving a message:

Pothos::packet pkt;
pkt.payload = myBuffer;
pkt.metadata["foo"] = bar;

//move the packet to the output port
outputPort->postMessage(std::move(pkt));

The buffer chunk object now supports an implicit cast operator to assign buffers to pointers without specifying any template arguments. While the old syntax is not going away, this is a slightly cleaner and preferred syntax used by the core blocks and examples.

Old syntax (still supported):

auto in = inputPort->buffer().as<const float *>();
auto out = outputPort->buffer().as<float *>();
for (size_t i = 0; i < N; i++) out[i] = in[i];

New implicit cast syntax:

const float *in = inputPort->buffer();
float *out = outputPort->buffer();
for (size_t i = 0; i < N; i++) out[i] = in[i];

Note: These changes mostly apply to internal library code, although it also cleans up syntax in a fair bit of block unit tests.

By adding implicit conversion for Object and Proxy classes, results of function calls can be assigned directly to the target type without providing explicit template arguments. With the implicit conversion support for Object.

Old syntax (still supported):

auto msgs = collector.call<std::vector<Pothos::Object>>("getMessages");

New implicit cast syntax:

std::vector<Pothos::Object> msgs = collector.call("getMessages");

Both Callable and Proxy classes have deprecated their clumsily named callVoid and callObject/callProxy variants for a single overloaded call() method which supports void, opaque, and type-specific returns.

Old syntax (deprecated):

auto expected = feeder.callProxy("feedTestPlan", testPlan.dump());
collector.callVoid("verifyTestPlan", expected);

New universal call syntax:

auto expected = feeder.call("feedTestPlan", testPlan.dump());
collector.call("verifyTestPlan", expected);