Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add WString support #372

Merged
merged 7 commits into from
May 2, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion rosidl_adapter/rosidl_adapter/msg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def convert_msg_to_idl(package_dir, package_name, input_file, output_dir):
'float32': 'float',
'float64': 'double',
'string': 'string',
'wstring': 'wstring',
}


Expand All @@ -69,6 +70,8 @@ def to_idl_literal(idl_type, value):
return 'TRUE' if value else 'FALSE'
if idl_type.startswith('string'):
return string_to_idl_string_literal(value)
if idl_type.startswith('wstring'):
return string_to_idl_wstring_literal(value)
return value


Expand All @@ -79,6 +82,10 @@ def string_to_idl_string_literal(string):
return '"{0}"'.format(estr)


def string_to_idl_wstring_literal(string):
return string_to_idl_string_literal(string)


def get_include_file(base_type):
if base_type.is_primitive_type():
return None
Expand All @@ -90,7 +97,10 @@ def get_idl_type(type_):
identifier = MSG_TYPE_TO_IDL[type_]
elif type_.is_primitive_type():
identifier = MSG_TYPE_TO_IDL[type_.type]
if identifier == 'string' and type_.string_upper_bound is not None:
if (
identifier in ('string', 'wstring') and
type_.string_upper_bound is not None
):
identifier += '<{type_.string_upper_bound}>'.format_map(locals())
else:
identifier = '{type_.pkg_name}::msg::{type_.type}' \
Expand Down
17 changes: 10 additions & 7 deletions rosidl_adapter/rosidl_adapter/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
'int64',
'uint64',
'string',
# TODO reconsider wstring / u16string / u32string
'wstring',
# TODO duration and time
'duration', # for compatibility only
'time', # for compatibility only
Expand Down Expand Up @@ -157,9 +157,12 @@ def __init__(self, type_string, context_package_name=None):
self.type = type_string
self.string_upper_bound = None

elif type_string.startswith('string%s' % STRING_UPPER_BOUND_TOKEN):
elif (
type_string.startswith('string%s' % STRING_UPPER_BOUND_TOKEN) or
type_string.startswith('wstring%s' % STRING_UPPER_BOUND_TOKEN)
):
self.pkg_name = None
self.type = 'string'
self.type = type_string.split(STRING_UPPER_BOUND_TOKEN, 1)[0]
upper_bound_string = type_string[len(self.type) +
len(STRING_UPPER_BOUND_TOKEN):]

Expand Down Expand Up @@ -321,7 +324,7 @@ def __eq__(self, other):

def __str__(self):
value = self.value
if self.type == 'string':
if self.type in ('string', 'wstring'):
value = "'%s'" % value
return '%s %s=%s' % (self.type, self.name, value)

Expand Down Expand Up @@ -356,7 +359,7 @@ def __str__(self):
s = '%s %s' % (str(self.type), self.name)
if self.default_value is not None:
if self.type.is_primitive_type() and not self.type.is_array and \
self.type.type == 'string':
self.type.type in ('string', 'wstring'):
s += " '%s'" % self.default_value
else:
s += ' %s' % self.default_value
Expand Down Expand Up @@ -571,7 +574,7 @@ def parse_value_string(type_, value_string):
"array value must start with '[' and end with ']'")
elements_string = value_string[1:-1]

if type_.type == 'string':
if type_.type in ('string', 'wstring'):
# String arrays need special processing as the comma can be part of a quoted string
# and not a separator of array elements
value_strings = parse_string_array_value_string(elements_string, type_.array_size)
Expand Down Expand Up @@ -725,7 +728,7 @@ def parse_primitive_value_string(type_, value_string):

return value

if primitive_type == 'string':
if primitive_type in ('string', 'wstring'):
# remove outer quotes to allow leading / trailing spaces in the string
for quote in ['"', "'"]:
if value_string.startswith(quote) and value_string.endswith(quote):
Expand Down
22 changes: 13 additions & 9 deletions rosidl_adapter/test/test_base_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,25 @@ def test_base_type_constructor():
'uint32',
'int64',
'uint64',
'string']
'string',
'wstring',
]
for primitive_type in primitive_types:
base_type = BaseType(primitive_type)
assert base_type.pkg_name is None
assert base_type.type == primitive_type
assert base_type.string_upper_bound is None

base_type = BaseType('string<=23')
assert base_type.pkg_name is None
assert base_type.type == 'string'
assert base_type.string_upper_bound == 23
for string_type in ('string', 'wstring'):
base_type = BaseType('%s<=23' % string_type)
assert base_type.pkg_name is None
assert base_type.type == string_type
assert base_type.string_upper_bound == 23

with pytest.raises(TypeError):
BaseType('string<=upperbound')
with pytest.raises(TypeError):
BaseType('string<=0')
with pytest.raises(TypeError):
BaseType('%s<=upperbound' % string_type)
with pytest.raises(TypeError):
BaseType('%s<=0' % string_type)

base_type = BaseType('pkg/Msg')
assert base_type.pkg_name == 'pkg'
Expand Down Expand Up @@ -84,3 +87,4 @@ def test_base_type_methods():
assert str(BaseType('pkg/Foo')) == 'pkg/Foo'
assert str(BaseType('bool')) == 'bool'
assert str(BaseType('string<=5')) == 'string<=5'
assert str(BaseType('wstring<=5')) == 'wstring<=5'
1 change: 1 addition & 0 deletions rosidl_adapter/test/test_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ def test_constant_methods():
assert str(Constant('bool', 'FOO', '1')) == 'bool FOO=True'

assert str(Constant('string', 'FOO', 'foo')) == "string FOO='foo'"
assert str(Constant('wstring', 'FOO', 'foo')) == "wstring FOO='foo'"
70 changes: 70 additions & 0 deletions rosidl_adapter/test/test_parse_primitive_value_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,76 @@ def test_parse_primitive_value_string_string():
assert value == '"foo"'


def test_parse_primitive_value_wstring_string():
value = parse_primitive_value_string(
Type('wstring'), 'foo')
assert value == 'foo'

value = parse_primitive_value_string(
Type('wstring'), '"foo"')
assert value == 'foo'

value = parse_primitive_value_string(
Type('wstring'), "'foo'")
assert value == 'foo'

value = parse_primitive_value_string(
Type('wstring'), '"\'foo\'"')
assert value == "'foo'"

value = parse_primitive_value_string(
Type('wstring'), '"foo ')
assert value == '"foo '

value = parse_primitive_value_string(
Type('wstring<=3'), 'foo')
assert value == 'foo'

with pytest.raises(InvalidValue):
parse_primitive_value_string(
Type('wstring<=3'), 'foobar')

with pytest.raises(InvalidValue):
parse_primitive_value_string(
Type('wstring'), r"""'foo''""")

with pytest.raises(InvalidValue):
parse_primitive_value_string(
Type('wstring'), r'''"foo"bar\"baz"''') # noqa: Q001

value = parse_primitive_value_string(
Type('wstring'), '"foo')
assert value == '"foo'

value = parse_primitive_value_string(
Type('wstring'), '"foo')
assert value == '"foo'

value = parse_primitive_value_string(
Type('wstring'), "'foo")
assert value == "'foo"

value = parse_primitive_value_string(
Type('wstring'), '"foo')
assert value == '"foo'

value = parse_primitive_value_string(
Type('wstring'), "'fo'o")
assert value == "'fo'o"

value = parse_primitive_value_string(
Type('wstring'), '"fo"o')
assert value == '"fo"o'

value = parse_primitive_value_string(
Type('wstring'), r'''"'foo'"''') # noqa: Q001
assert value == "'foo'"

value = parse_primitive_value_string(
Type('wstring'), r"""'"foo"'""")
assert value == '"foo"'


def test_parse_primitive_value_string_unknown():
class CustomType(Type):

Expand Down
1 change: 1 addition & 0 deletions rosidl_generator_c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ if(BUILD_TESTING)
"msg/Uint8.msg"
"msg/Various.msg"
"msg/Wire.msg"
"msg/WStrings.msg"
"srv/AddTwoInts.srv"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ bool
rosidl_generator_c__U16String__assignn(
rosidl_generator_c__U16String * str, const uint16_t * value, size_t n);

ROSIDL_GENERATOR_C_PUBLIC
bool
rosidl_generator_c__U16String__assignn_from_char(
rosidl_generator_c__U16String * str, const char * value, size_t n);

ROSIDL_GENERATOR_C_PUBLIC
bool
rosidl_generator_c__U16String__assign(
Expand All @@ -54,6 +59,11 @@ ROSIDL_GENERATOR_C_PUBLIC
size_t
rosidl_generator_c__U16String__len(const uint16_t * value);

ROSIDL_GENERATOR_C_PUBLIC
bool
rosidl_generator_c__U16String__resize(
rosidl_generator_c__U16String * str, size_t n);

ROSIDL_GENERATOR_C_PUBLIC
bool
rosidl_generator_c__U16String__Sequence__init(
Expand Down
8 changes: 8 additions & 0 deletions rosidl_generator_c/msg/WStrings.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
wstring empty_wstring
wstring def_wstring "Hello world!"
wstring def_wstring2 "Hello'world!"
wstring def_wstring3 'Hello"world!'
wstring def_wstring4 'Hello\'world!'
wstring def_wstring5 "Hello\"world!"
wstring<=22 ub_wstring
wstring<=22 ub_def_wstring "Upper bounded string."
7 changes: 7 additions & 0 deletions rosidl_generator_c/rosidl_generator_c/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ def value_to_c(type_, value):
if isinstance(type_, AbstractString):
return '"%s"' % escape_string(value)

if isinstance(type_, AbstractWString):
return 'u"%s"' % escape_wstring(value)

return basic_value_to_c(type_, value)


Expand Down Expand Up @@ -175,3 +178,7 @@ def escape_string(s):
s = s.replace('\\', '\\\\')
s = s.replace('"', r'\"')
return s


def escape_wstring(s):
return escape_string(s)
34 changes: 34 additions & 0 deletions rosidl_generator_c/src/u16string_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ rosidl_generator_c__U16String__assignn(
return true;
}

bool
rosidl_generator_c__U16String__assignn_from_char(
rosidl_generator_c__U16String * str, const char * value, size_t n)
{
// since n represents the number of 8-bit characters it must be an even number
if (n % 2 != 0) {
return false;
}
return rosidl_generator_c__U16String__assignn(
str, (const uint16_t *)value, n / 2);
}

bool
rosidl_generator_c__U16String__assign(
rosidl_generator_c__U16String * str, const uint16_t * value)
Expand All @@ -115,6 +127,28 @@ rosidl_generator_c__U16String__len(const uint16_t * value)
return len;
}

bool
rosidl_generator_c__U16String__resize(
rosidl_generator_c__U16String * str, size_t n)
{
if (!str) {
return false;
}
// check valid range of n before allocating n + 1 characters
if (n > SIZE_MAX / sizeof(uint16_t) - 1) {
return false;
}
uint16_t * data = realloc(str->data, (n + 1) * sizeof(uint16_t));
jacobperron marked this conversation as resolved.
Show resolved Hide resolved
if (!data) {
return false;
}
data[n] = 0;
str->data = data;
str->size = n;
str->capacity = n + 1;
return true;
}

bool
rosidl_generator_c__U16String__Sequence__init(
rosidl_generator_c__U16String__Sequence * sequence, size_t size)
Expand Down
37 changes: 36 additions & 1 deletion rosidl_generator_c/test/test_interfaces.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "rosidl_generator_c/primitives_sequence_functions.h"
#include "rosidl_generator_c/string_functions.h"
#include "rosidl_generator_c/u16string_functions.h"

#include "rosidl_generator_c/msg/bool.h"
#include "rosidl_generator_c/msg/bounded_array_nested.h"
Expand Down Expand Up @@ -56,13 +57,16 @@
#include "rosidl_generator_c/msg/uint8.h"
#include "rosidl_generator_c/msg/various.h"
#include "rosidl_generator_c/msg/wire.h"
#include "rosidl_generator_c/msg/w_strings.h"

#define TEST_STRING \
"Deep into that darkness peering, long I stood there wondering, fearing"
#define TEST_STRING2 \
"The quick brown fox jumps over the lazy dog."
#define TEST_STRING3 \
"PACK MY BOX WITH FIVE DOZEN LIQUOR JUGS."
#define TEST_WSTRING \
u"Deep into that darkness peering, long I stood there wondering, fearing \u2122"
#define ARRAY_SIZE 7

#define STRINGIFY(x) _STRINGIFY(x)
Expand Down Expand Up @@ -333,8 +337,39 @@ int test_strings(void)
return 0;
}


/**
* Test message with different wstring types.
*/
int test_wstrings(void)
{
bool res = false;
rosidl_generator_c__msg__WStrings * wstrings = NULL;

wstrings = rosidl_generator_c__msg__WStrings__create();
EXPECT_NE(wstrings, NULL);

res = rosidl_generator_c__U16String__assign(&wstrings->empty_wstring, TEST_WSTRING);
EXPECT_EQ(true, res);
// EXPECT_EQ(0, strcmp(wstrings->empty_wstring.data, TEST_WSTRING));
dirk-thomas marked this conversation as resolved.
Show resolved Hide resolved
// EXPECT_EQ(0, strcmp(wstrings->def_wstring.data, "Hello world!"));
// EXPECT_EQ(0, strcmp(wstrings->def_wstring2.data, "Hello'world!"));
// EXPECT_EQ(0, strcmp(wstrings->def_wstring3.data, "Hello\"world!"));
// EXPECT_EQ(0, strcmp(wstrings->def_wstring4.data, "Hello'world!"));
// EXPECT_EQ(0, strcmp(wstrings->def_wstring5.data, "Hello\"world!"));
// since upper-bound checking is not implemented yet, we restrict the string copying
// TODO(mikaelarguedas) Test string length properly instead of cheating copy
dirk-thomas marked this conversation as resolved.
Show resolved Hide resolved
// res = rosidl_generator_c__String__assign(&wstrings->ub_wstring, TEST_WSTRING);
res = rosidl_generator_c__U16String__assignn(&wstrings->ub_wstring, TEST_WSTRING, 24);
EXPECT_EQ(true, res);
// EXPECT_EQ(0, strcmp(wstrings->ub_wstring.data, "Deep into that darknes"));
// EXPECT_EQ(0, strcmp(wstrings->ub_def_wstring.data, "Upper bounded string."));

rosidl_generator_c__msg__WStrings__destroy(wstrings);

return 0;
}

/**
* Test message with different string array types.
*/
int test_string_arrays(void)
Expand Down
Loading