From GNU Radio
Revision as of 01:16, 20 March 2020 by 777arc (talk | contribs)
Jump to navigation Jump to search

Polymorphic Type

A new data type is a polymorphic data type. The type can really be used to store anything, but also has simple conversion methods for common data types such as bool, long, or a vector.

The polymorphic type simplifies message passing between blocks, as all of the data is of the same type, including the message. Tags also use PMTs as data type, so a stream tag can be of any logical data type. In a sense, PMTs are a way to extend C++' strict typing with something more flexible.


PMT Types

The PMT library supports the following major types. Some of the more complex types have their own wiki page for assistance.

' bool - just like a C++ boolean, true or false

' symbol - easiest to think of it as a std::string

' integer - int, long, unsigned

' real - double, float

' complex - gr_complex, std::complex

' null - NULL

' pair - a 2-tuple data type

' list - a list

' vector - a vector of PMT objects

' dict - a dictionary (hash) like structure

' uniform_vector - a vector of typed objects (uint8_t, float, etc...)

' any - boost:any cast

Making a PMT instance

To make PMT, the data type pmt_t is used just like any other data type.

The following is an example:

pmt_t p_instance;

Constructing/Assigning a PMT

To construct or assign a value to a PMT, a method is available for each type of the following syntax: pmt<type>_.

The following constructors are available, all of which return pmt_t:

' pmt_bool()

' pmt_symbol(const std::string &)

' pmt_integer(long)

' pmt_real(double)

' pmt_complex(std::complex<double>)

' pmt_null()

' pmt_pair() or pmt_pair(pmt,pmt)

' pmt_vector(size_t,pmt)

' pmt_dict()

' pmt_make_'vector(size_t,) -- see uniform vectors

' pmt_any(const boost::any &)

For example, pmt_integer(long) returns a pmt_t, constructed from a long:

pmt_t p_num = pmt_integer(3);

Checking PMT Type

With a polymorphic type, it is useful to have methods to check what type it actually is if unknown.

The following methods are available for testing PMT types, all of which return bool:

' pmt_is_bool(pmt)

' pmt_is_symbol(pmt)

' pmt_is_number(pmt)

' pmt_is_integer(pmt)

' pmt_is_real(pmt)

' pmt_is_complex(pmt)

' pmt_is_null(pmt)

' pmt_is_pair(pmt)

' pmt_is_vector(pmt)

' pmt_is_uniform_vector(pmt)

' pmt_is_dict(pmt)

' pmt_is_any(pmt)

In this example, test_pmt will be assigned the value true:

pmt_t p_num = pmt_integer(3);
bool test_pmt = pmt_is_integer(p_num);

PMT Constants

There are 3 main constant values in the PMT world which can be returned:

' PMT_T - PMT representation of true

' PMT_F - PMT representation of false

' PMT_NIL - PMT NIL/NULL representation

When a PMT is initialized with no value, such as pmt_dict() ... it is equivalent to PMT_NIL until it is assigned elements.

Testing Equality

To test the equality of PMT types, the following methods are available.

' pmt_eq(pmt_t, pmt_t) - tests strict equality (the two parameters are the same)

' pmt_eqv(pmt_t, pmt_t) - tests for equivalence (the two parameters have the same value)

' pmt_equal(pmt_t, pmt_t) - tests for equivalence of multi-value types (pmt_pair, pmt_vector)

The following gives a good overview of equality testing:

pmt_t p1 = pmt_integer(5);
pmt_t p2 = pmt_integer(5);
pmt_t p3 = pmt_integer(1);

pmt_eq(p1, p1);  // Returns true
pmt_eq(p1, p2);  // Returns false

pmt_eqv(p1, p1); // Returns true
pmt_eqv(p1, p2); // Returns true

pmt_t p3 = pmt_pair(p1, p1);
pmt_t p4 = pmt_pair(p1, p2);
pmt_t p5 = pmt_pair(p1, p3);

pmt_equal(p3, p4);  // Returns true, equivalence in values
pmt_equal(p3, p5);  // Returns false, values are different

PMT Conversion Methods

There are simple methods to store common C++ types as PMT for use in passing the data in m-block messages.

All of the conversion methods are of a similar syntax:

' pmt_from<type>_ - convert a C++ type to a PMT

' pmt_to<type>_ - convert a PMT to a C++ type

The following C++ types are supported:

' bool, long, double, complex

For example:

pmt_t p_val = pmt_from_long(3);
long l_val = pmt_to_long(p_val);

In Python, you can use the pmt.to_python() and pmt.to_pmt() methods to convert, without having to know the type yourself (see below).

Checking PMT Length

For variable length PMT types, such as list and vector, you can use the following method to check how many elements are in the PMT:

' size_t pmt_length(pmt_t)

pmt_t p1 = pmt_integer(1);
pmt_t p2 = pmt_integer(2);
pmt_t p3 = pmt_integer(3);

pmt_t p_list = pmt_list3(p1, p2, p3);

pmt_length(p_list);  // Returns 3

Detailed Type Information


PMT lists are as they sound, lists! Each list element is of type pmt_t, and the list can be of infinite length.

To create a base list, you can use the following methods:

' pmt_list1(pmt_t)

' pmt_list2(pmt_t, pmt_t)

' pmt_list3(pmt_t, pmt_t, pmt_t)

' pmt_list4(pmt_t, pmt_t, pmt_t, ...)

' pmt_list5(pmt_t, pmt_t, pmt_t, ...)

' pmt_list6(pmt_t, pmt_t, pmt_t, ...)

Anything beyond 6, use an add, which returns a new list:

' pmt_t pmt_list_add(pmt_t list, pmt_t object)

List items cannot be removed, as they are implemented as constants.

To access the nth element in a list, the following method is available which returns the nth element:

' pmt_t pmt_nth(size_t n, pmt_t list)

The following exercises PMT lists:

pmt_t p_list1 = pmt_list1(PMT_T);
p_list1 = pmt_list_add(p_list1, PMT_F);

pmt_t p_list2 = pmt_list2(PMT_T, PMT_F);

pmt_equal(p_list1, p_list2);  // Returns true

pmt_t p_element0 = pmt_nth(0, p_list2);  // Access elements
pmt_t p_element1 = pmt_nth(1, p_list2);

pmt_eqv(p_element0, PMT_T);  // Returns true
pmt_eqv(p_element1, PMT_F);  // Returns true

Uniform Vectors

Uniform PMT vectors are for storing vectors of common C++ types. The following types are supported:

' uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double, std::complex<float>, std::complex<double>

The following methods are used to create a uniform vector, whose return types are pmt_t, where the first parameter is the number of elements to create and the second parameter is a fill parameter for initializing all of the elements to a specific value:

' pmt_make_u8vector(size_t, uint8_t)

' pmt_make_s8vector(size_t, int8_t)

' pmt_make_u16vector(size_t, uint16_t)

' pmt_make_s16vector(size_t, int16_t)

' pmt_make_u32vector(size_t, uint32_t)

' pmt_make_s32vector(size_t, int32_t)

' pmt_make_u64vector(size_t, uint64_t)

' pmt_make_s64vector(size_t, int64_t)

' pmt_make_f32vector(size_t, float);

' pmt_make_f64vector(size_t, double);

' pmt_make_c32vector(size_t, std::complex<float>)

' pmt_make_c64vector(size_t, std::complex<double>)

To get a reference to each vector to access it like an array, you can use the following, which takes a uniform vector as parameter 1, and a pointer to a size_t to return the number of elements in the vector:

' pmt_<type>vector_ref(pmt_t, size_t)

The following is an example of uniform vectors:

static const int nelements = 64;
pmt_t s16vec = pmt_make_s16vector(nelements, 0);  // Initializes all 64 elements to 0

size_t vec_size;
int16_t *elements = pmt_s16vector_writable_elements(s16vec, vec_size);  // Returns pointer, vec_size is set to 64

// Now you can access the elements like a standard array
for(int i=0; i


A dictionary behaves exactly like a hash. PMT objects are referenced with a pmt_symbol (string-like PMT type).

To create a dictionary, use:

' pmt_t pmt_make_dict()

To set items in a dictionary, use:

' dict = pmt_dict_add(pmt_t dict, pmt_t symbol, pmt_t value)

You can reference items in a dictionary using the following, which returns a specified error_val if the symbol (hash key) did not exist:

' pmt_dict_ref(pmt_t dict, pmt_t symbol, pmt_t error_val)

It is always useful to check that a pmt_t is a dictionary before trying to reference its elements to prevent exceptions:

' pmt_is_dict(pmt_t)

The following is an example of using dictionaries:

pmt_t p_dict = pmt_make_dict();  // Empty dictionary

p_dict = pmt_dict_add(p_dict, pmt_symbol("number of dogs"), pmt_integer(3));
p_dict = pmt_dict_add(p_dict, pmt_symbol("number of cats"), pmt_integer(5));
p_dict = pmt_dict_add(p_dict, pmt_symbol("has a dog"), PMT_T);
p_dict = pmt_dict_add(p_dict, pmt_symbol("has a turtle"), PMT_F);

if(pmt_is_dict(p_dict)) {
  pmt_t p_ndogs = pmt_dict_ref(p_dict, pmt_symbol("number of dogs"), PMT_NIL);
  pmt_t p_ncats = pmt_dict_ref(p_dict, pmt_symbol("number of cats"), PMT_NIL);
  pmt_t p_dog = pmt_dict_ref(p_dict, pmt_symbol("has a dog"), PMT_NIL);
  pmt_t p_turtle = pmt_dict_ref(p_dict, pmt_symbol("has a turtle"), PMT_NIL);

  std::cout << "I have " << pmt_to_long(p_ndogs) << " dogs!\n";
  std::cout << "I have " << pmt_to_long(p_ncats) << " cats!\n";

  if(pmt_eqv(p_dog, PMT_T))
    std::cout << "I have a dog\n";       // This will be outputted
    std::cout << " I do not have a dog\n";

  if(pmt_eqv(p_turtle, PMT_T))
    std::cout << "I have a turtle\n";
    std::cout << "I do not have a turtle\n";  // This will be outputted


When there is nothing to store your type, use a pmt_any. This allows you to cast anything into a pmt_t using boost::any cast.


typedef struct d_frame_hdr_t {
  long src_addr;
  long dst_addr;
} +attribute+((+packed+));

d_frame_hdr_t my_header;
my_header.src_addr = 3;
my_header.dst_addr = 4;

pmt_t p_any = pmt_make_any(my_header);

// After extracting using pmt_any_ref the following will be true:
//   extract_header.src_addr == 3
//   extract_header.dst_addr == 4
d_frame_hdr_t extract_header = boost::any_cast (pmt_any_ref(p_any));

Conversion between Python Objects and PMTs

Although PMTs can be manipulating in python using the python versions of the C++ interfaces there are some additional goodies that make it easier to work with PMTs in python.
In particular there are functions to automate the conversion of python booleans, strings, integers, longs, floats, complex numbers, dictionaries, lists, tuples and combinations thereof into their equivalent PMTs.
Two functions capture most of this functionality.

  • pmt.to_pmt - Converts a python object to a PMT.
  • pmt.to_python - Converts a PMT into a python object.