blob: f21787518870be08e5574b39502c0df466692062 [file] [log] [blame]
/***************************************************************************
*
* 26.valarray.cassign.cpp - tests exercising valarray computed assignment
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Copyright (C) 2007-2008 Rogue Wave Software, Inc.
*
**************************************************************************/
#include <cstdlib> // for free(), strtod(), size_t
#include <valarray> // for indirect_array, valarray
#include <rw_value.h> // for UserClass
#include <rw_driver.h> // for rw_test()
#include <rw_printf.h> // for rw_asnprintf()
/**************************************************************************/
// returns an array of size elements of type T constructed from a string
// of comma-separated values
template <class T>
T*
make_array (const T*, const char *s, std::size_t *psize)
{
std::size_t bufsize = 0; // capacity of buffer
std::size_t nelems = 0; // number of elements in buffer
if (psize)
*psize = 0;
if (0 == s)
s = "";
T* buf = 0;
while (*s) {
// get the next value from the string
char *end = 0;
const double val = std::strtod (s, &end);
unsigned long repeat = 1;
if ('@' == *end) {
// process repeat directive (e.g., "123@5" expands into
// 5 copies of the number 123)
char *e = 0;
repeat = std::strtoul (++end, &e, 10);
// skip trailing whitespace
for (; ' ' == *end; ++end);
// the next character must be either a NUL or comma
RW_ASSERT ('\0' == *e || ',' == *e);
if (',' == *e)
++e;
s = e;
}
else {
// skip trailing whitespace
for (; ' ' == *end; ++end);
// the next character must be NUL or a comma
RW_ASSERT ('\0' == *end || ',' == *end);
s = end;
if (*s)
++s;
}
while (repeat--) {
if (nelems == bufsize) {
// reallocate buffer
const std::size_t newsize = (bufsize + 1) * 2;
T* const tmp = new T [newsize];
// copy data to the newly allocated buffer
for (std::size_t i = 0; i != nelems; ++i)
tmp [i] = buf [i];
// increase the capacity
bufsize = newsize;
// deallocate old buffer
delete[] buf;
buf = tmp;
}
// append value to the end of the buffer
buf [nelems++] = T (val);
}
}
if (psize)
*psize = nelems;
return buf;
}
// deletes an array of elements of type T returned from make_array
template <class T>
void
delete_array (const T *array, std::size_t)
{
T* const a = _RWSTD_CONST_CAST (T*, array);
delete[] a;
}
template <class T>
const std::size_t* count (const T*) { return 0; }
template <class T>
T value (const T &val) { return val; }
/**************************************************************************/
// deletes an array of elements of type T returned from make_array
void
delete_array (const UserClass *array, std::size_t nelems)
{
UserClass* const a = _RWSTD_CONST_CAST (UserClass*, array);
for (std::size_t i = 0; i != nelems; ++i)
(a + i)->~UserClass ();
operator delete (a);
}
// returns an array of size elements of type UserClass
// constructed from a string of comma-separated values
UserClass*
make_array (const UserClass*, const char *s, std::size_t *psize)
{
std::size_t bufsize = 0; // capacity of buffer
std::size_t nelems = 0; // number of elements in buffer
if (psize)
*psize = 0;
if (0 == s)
s = "";
UserClass* buf = 0;
while (*s) {
// get the next value from the string
char *end = 0;
const double val = std::strtod (s, &end);
unsigned long repeat = 1;
if ('@' == *end) {
// process repeat directive (e.g., "123@5" expands into
// 5 copies of the number 123)
char *e = 0;
repeat = std::strtoul (++end, &e, 10);
// skip trailing whitespace
for (; ' ' == *end; ++end);
// the next character must be either a NUL or comma
RW_ASSERT ('\0' == *e || ',' == *e);
if (',' == *e)
++e;
s = e;
}
else {
// skip trailing whitespace
for (; ' ' == *end; ++end);
// the next character must be NUL or a comma
RW_ASSERT ('\0' == *end || ',' == *end);
s = end;
if (*s)
++s;
}
while (repeat--) {
if (nelems == bufsize) {
static const std::size_t size = sizeof (UserClass);
void* const raw = operator new ((bufsize + 1) * 2 * size);
UserClass* const tmp = _RWSTD_STATIC_CAST (UserClass*, raw);
for (std::size_t i = 0; i != nelems; ++i)
new (tmp +i) UserClass (buf [i]);
bufsize = (bufsize + 1) * 2;
delete_array (buf, nelems);
buf = tmp;
}
new (buf + nelems) UserClass ();
buf [nelems].data_.val_ = int (val);
++nelems;
}
}
if (psize)
*psize = nelems;
return buf;
}
const std::size_t* count (const UserClass*) { return &UserClass::count_; }
int value (const UserClass &val) { return val.data_.val_; }
/**************************************************************************/
template <class T>
void
test_op_assign (const T*,
std::valarray<T>&
(std::valarray<T>::*op_assign)(const std::valarray<T>&),
const char *tname, // T's type name
const char *opname, // which assignment operator
int line, // test case line number
const char *lhs_str, // left hand side of assignment
const char *rhs_str, // right hand side of assignment
const char *res_str) // result of assignment
{
std::size_t nelems = 0;
std::size_t tmp;
// create an array of values of type T from the string lhs_str
// representing the left-hand side argument of the assignment
const T* const lhs_array = make_array ((const T*)0, lhs_str, &nelems);
// create an array of values of type T from the string rhs_str
// representing the right-hand side argument of the assignment
const T* const rhs_array = make_array ((const T*)0, rhs_str, &tmp);
// both arguments of the assignment must have the same size
RW_ASSERT (tmp == nelems);
// create an array of values of type T from the string res_str
// representing the result of the assignment
const T* const res_array = make_array ((const T*)0, res_str, &tmp);
// the result of the assignment must have the same size as both
// arguments
RW_ASSERT (tmp == nelems);
// construct valarray arguments from the arrays created above
/* const */ std::valarray<T> lhs_va (lhs_array, nelems);
const std::valarray<T> rhs_va (rhs_array, nelems);
char* fname = 0;
std::size_t size = 0;
// pointer to a counter keeping track of all objects of type T
// in existence (non-null only for T=UserClass)
const std::size_t* const pcounter = count ((const T*)0);
// get the number of objects of type T (only possible for user
// defined T) before invoking the operator
std::size_t nobjects = pcounter ? *pcounter : 0;
// format the name of the function call to be used in diagnostic
// messages below
rw_asnprintf (&fname, &size,
"valarray<%s>(%s) %s std::valarray<%1$s>(%s)",
tname, lhs_str, opname, rhs_str);
// invoke the assignment operator through the member pointer
std::valarray<T> &res = (lhs_va.*op_assign)(rhs_va);
// verify that the resturned reference refers to the assignee
rw_assert (&res == &lhs_va, 0, line,
"line %d == %#p, got %#p",
__LINE__, fname, &lhs_va, &res);
// verify the size of the array
rw_assert (lhs_va.size () == nelems, 0, line,
"line %d. %s.size() == %zu, got %zu",
__LINE__, fname, nelems, lhs_va.size ());
if (pcounter) {
// verify that the assignment didn't leak any objects
nobjects = *pcounter - nobjects;
rw_assert (0 == nobjects, 0, line,
"line %d. %s constucted %zu objects, expected %zu",
__LINE__, fname, nobjects, nelems);
}
// verify the element values
for (std::size_t i = 0; i != nelems; ++i) {
if (!rw_assert (lhs_va [i] == res_array [i], 0, line,
"line %d. %s: element at index %zu == %d, got %d",
__LINE__, fname, i, value (res_array [i]),
value (lhs_va [i])))
break;
}
delete_array (lhs_array, nelems);
delete_array (rhs_array, nelems);
std::free (fname);
}
/**************************************************************************/
#define OP(lhs, rhs, res) \
test_op_assign ((T*)0, op, tname, opname, __LINE__, lhs, rhs, res)
template <class T>
void test_mul_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator*=;
OP ("", "", "");
OP ("0", "0", "0");
OP ("0", "1", "0");
OP ("1", "0", "0");
OP ("1", "1", "1");
OP ("1", "2", "2");
OP ("1,2", "1,1", "1,2");
OP ("1,2", "1,2", "1,4");
OP ("2,3", "4,5", "8,15");
OP ("3,4", "5,6", "15,24");
OP ("1,2,3", "4,5,6", "4,10,18");
OP ("-1,-2,-3", "-4,-5,-6", "4,10,18");
OP ("1,-2,3", "-4,5,-6", "-4,-10,-18");
}
/**************************************************************************/
template <class T>
void test_div_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator/=;
OP ("", "", "");
OP ("0", "1", "0");
OP ("1", "1", "1");
OP ("2", "1", "2");
OP ("2", "2", "1");
OP ("3", "1", "3");
OP ("3", "2", "1.5");
OP ("4", "2", "2");
OP ("0,1,2,3,4,5,6,7", "1,1,1,1,1,1,1,1", "0,1,2,3,4,5,6,7");
OP ("2,4,6,8,10,12,14,16", "2,2,2,2,2,2,2,2", "1,2,3,4,5,6,7,8");
OP ("2,4,6,8,10,12,14,16", "2,2,2,2,2,2,2,2", "1,2,3,4,5,6,7,8");
OP ("0@127", "1@127", "0@127");
OP ("1@128", "1@128", "1@128");
OP ("2@255", "1@255", "2@255");
OP ("2@256", "2@256", "1@256");
OP ("3@256", "1@256", "3@256");
OP ("3@511", "2@511", "1.5@511");
OP ("4@512", "2@512", "2@512");
}
/**************************************************************************/
template <class T>
void test_mod_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator%=;
OP ("", "", "");
OP ("0", "1", "0");
OP ("1", "1", "0");
OP ("2", "1", "0");
OP ("2", "2", "0");
OP ("3", "1", "0");
OP ("3", "2", "1");
OP ("3", "3", "0");
OP ("4", "1", "0");
OP ("4", "2", "0");
OP ("4", "3", "1");
OP ("4", "4", "0");
OP ("0,1", "2,2", "0,1");
OP ("1,2,3", "4,4,4", "1,2,3");
OP ("2,3,4,5", "6,6,6,6", "2,3,4,5");
OP ("0,1,2,3,4,5,6,7,8,9", "10,9,8,7,6,5,4,3,2,1", "0,1,2,3,4,0,2,1,0,0");
}
#define CANNOT_TEST(T, name) \
void test_##name (const T*, const char*, const char*) { } \
typedef void swallow_semicolon
CANNOT_TEST (float, mod_assign);
CANNOT_TEST (double, mod_assign);
CANNOT_TEST (UserClass, mod_assign);
/**************************************************************************/
template <class T>
void test_add_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator+=;
OP ("", "", "");
OP ("0", "0", "0");
OP ("0", "1", "1");
OP ("1", "0", "1");
OP ("1", "-1", "0");
OP ("-1", "1", "0");
OP ("0@31", "0@31", "0@31");
OP ("0@31", "1@31", "1@31");
OP ("1@31", "0@31", "1@31");
OP ("1@31", "-1@31", "0@31");
OP ("-1@31", "1@31", "0@31");
OP ("2@32", "3@32", "5@32");
OP ("3@32", "5@32", "8@32");
OP ("5@32", "7@32", "12@32");
OP ("9@32", "-9@32", "0@32");
OP ("0@63", "0@63", "0@63");
OP ("0@63", "1@63", "1@63");
OP ("1@63", "0@63", "1@63");
OP ("1@63", "-1@63", "0@63");
OP ("-1@63", "1@63", "0@63");
OP ("0@63", "0@63", "0@63");
OP ("0@63", "1@63", "1@63");
OP ("1@63", "0@63", "1@63");
OP ("1@63", "-1@63", "0@63");
OP ("-1@63", "1@63", "0@63");
OP ("0,0", "0,0", "0,0");
OP ("0,0", "0,1", "0,1");
OP ("0,0", "1,0", "1,0");
OP ("0,0", "1,1", "1,1");
OP ("0,1", "0,1", "0,2");
OP ("0,1", "1,0", "1,1");
OP ("0,1", "1,1", "1,2");
OP ("1,0", "0,0", "1,0");
OP ("1,0", "0,1", "1,1");
OP ("1,0", "1,0", "2,0");
OP ("1,0", "1,1", "2,1");
OP ("1,1", "0,0", "1,1");
OP ("1,1", "0,1", "1,2");
OP ("1,1", "1,0", "2,1");
OP ("1,1", "1,1", "2,2");
}
/**************************************************************************/
template <class T>
void test_sub_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator-=;
OP ("", "", "");
OP (" 0", " 0", " 0");
OP (" 0", "+1", "-1");
OP ("+1", " 0", "+1");
OP ("+1", "-1", "+2");
OP ("-1", "+1", "-2");
OP (" 0@12", " 0@12", " 0@12");
OP (" 0@23", "+1@23", "-1@23");
OP ("+1@34", " 0@34", "+1@34");
OP ("+1@45", "-1@45", "+2@45");
OP ("-1@56", "+1@56", "-2@56");
}
/**************************************************************************/
template <class T>
void test_xor_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator^=;
OP ("", "", "");
OP ("0", "0", "0");
OP ("0", "1", "1");
OP ("1", "0", "1");
OP ("1", "1", "0");
OP ("3", "0", "3");
OP ("3", "1", "2");
OP ("3", "2", "1");
OP ("3", "3", "0");
OP ("0@10", "0@10", "0@10");
OP ("0@11", "1@11", "1@11");
OP ("1@12", "0@12", "1@12");
OP ("1@13", "1@13", "0@13");
OP ("3@14", "0@14", "3@14");
OP ("3@15", "1@15", "2@15");
OP ("3@16", "2@16", "1@16");
OP ("3@17", "3@17", "0@17");
}
CANNOT_TEST (float, xor_assign);
CANNOT_TEST (double, xor_assign);
CANNOT_TEST (UserClass, xor_assign);
/**************************************************************************/
template <class T>
void test_and_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator&=;
OP ("", "", "");
OP ("0", "0", "0");
OP ("0", "1", "0");
OP ("1", "0", "0");
OP ("1", "1", "1");
OP ("3", "0", "0");
OP ("3", "1", "1");
OP ("3", "2", "2");
OP ("3", "3", "3");
OP ("0@10", "0@10", "0@10");
OP ("0@11", "1@11", "0@11");
OP ("1@12", "0@12", "0@12");
OP ("1@13", "1@13", "1@13");
OP ("3@14", "0@14", "0@14");
OP ("3@15", "1@15", "1@15");
OP ("3@16", "2@16", "2@16");
OP ("3@17", "3@17", "3@17");
}
CANNOT_TEST (float, and_assign);
CANNOT_TEST (double, and_assign);
CANNOT_TEST (UserClass, and_assign);
/**************************************************************************/
template <class T>
void test_or_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator|=;
OP ("", "", "");
OP ("0", "0", "0");
OP ("0", "1", "1");
OP ("1", "0", "1");
OP ("1", "1", "1");
OP ("3", "0", "3");
OP ("3", "1", "3");
OP ("3", "2", "3");
OP ("3", "3", "3");
OP ("0@10", "0@10", "0@10");
OP ("0@11", "1@11", "1@11");
OP ("1@12", "0@12", "1@12");
OP ("1@13", "1@13", "1@13");
OP ("3@14", "0@14", "3@14");
OP ("3@15", "1@15", "3@15");
OP ("3@16", "2@16", "3@16");
OP ("3@17", "3@17", "3@17");
}
CANNOT_TEST (float, or_assign);
CANNOT_TEST (double, or_assign);
CANNOT_TEST (UserClass, or_assign);
/**************************************************************************/
template <class T>
void test_shl_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator<<=;
OP ("", "", "");
OP ("0", "0", "0");
OP ("0", "1", "0");
OP ("1", "0", "1");
OP ("1", "1", "2");
OP ("3", "0", "3");
OP ("3", "1", "6");
OP ("3", "2", "12");
OP ("3", "3", "24");
OP ("0@10", "0@10", "0@10");
OP ("0@11", "1@11", "0@11");
OP ("1@12", "0@12", "1@12");
OP ("1@13", "1@13", "2@13");
OP ("3@14", "0@14", "3@14");
OP ("3@15", "1@15", "6@15");
OP ("3@16", "2@16", "12@16");
OP ("3@17", "3@17", "24@17");
}
CANNOT_TEST (float, shl_assign);
CANNOT_TEST (double, shl_assign);
CANNOT_TEST (UserClass, shl_assign);
/**************************************************************************/
template <class T>
void test_shr_assign (const T*, const char *tname, const char *opname)
{
rw_info (0, 0, __LINE__,
"std::valarray<%s>::operator%s (const std::valarray<%1$s>&)",
tname, opname);
std::valarray<T>& (std::valarray<T>::*op)(const std::valarray<T>&) =
&std::valarray<T>::operator>>=;
OP ("", "", "");
OP ("0", "0", "0");
OP ("0", "1", "0");
OP ("1", "0", "1");
OP ("2", "1", "1");
OP ("3", "0", "3");
OP ("6", "1", "3");
OP ("12", "2", "3");
OP ("24", "3", "3");
OP ("0@21", "0@21", "0@21");
OP ("0@21", "1@21", "0@21");
OP ("1@21", "0@21", "1@21");
OP ("2@21", "1@21", "1@21");
OP ("3@21", "0@21", "3@21");
OP ("6@21", "1@21", "3@21");
OP ("12@21", "2@21", "3@21");
OP ("24@21", "3@21", "3@21");
}
CANNOT_TEST (float, shr_assign);
CANNOT_TEST (double, shr_assign);
CANNOT_TEST (UserClass, shr_assign);
/**************************************************************************/
enum {
inx_mul_assign,
inx_div_assign,
inx_mod_assign,
inx_add_assign,
inx_sub_assign,
inx_xor_assign,
inx_and_assign,
inx_or_assign,
inx_shl_assign,
inx_shr_assign,
n_assign_ops
};
static int opt_assign [n_assign_ops];
template <class T>
void test_op_assign (const T*, const char *tname)
{
const std::size_t nassigns = sizeof opt_assign / sizeof *opt_assign;
for (std::size_t i = 0; i != nassigns; ++i) {
if (0 < opt_assign [i]) {
for (std::size_t j = 0; j != nassigns; ++j) {
if (0 == opt_assign [j])
opt_assign [j] = -1;
}
break;
}
}
if (rw_note (0 <= opt_assign [inx_mul_assign], 0, __LINE__,
"tests of operator*= disabled"))
test_mul_assign ((const T*)0, tname, "*=");
if (rw_note (0 <= opt_assign [inx_div_assign], 0, __LINE__,
"tests of operator/= disabled"))
test_div_assign ((const T*)0, tname, "/=");
if (rw_note (0 <= opt_assign [inx_mod_assign], 0, __LINE__,
"tests of operator%= disabled"))
test_mod_assign ((const T*)0, tname, "%=");
if (rw_note (0 <= opt_assign [inx_add_assign], 0, __LINE__,
"tests of operator+= disabled"))
test_add_assign ((const T*)0, tname, "+=");
if (rw_note (0 <= opt_assign [inx_sub_assign], 0, __LINE__,
"tests of operator-= disabled"))
test_sub_assign ((const T*)0, tname, "-=");
if (rw_note (0 <= opt_assign [inx_xor_assign], 0, __LINE__,
"tests of operator^= disabled"))
test_xor_assign ((const T*)0, tname, "^=");
if (rw_note (0 <= opt_assign [inx_and_assign], 0, __LINE__,
"tests of operator&= disabled"))
test_and_assign ((const T*)0, tname, "&=");
if (rw_note (0 <= opt_assign [inx_or_assign], 0, __LINE__,
"tests of operator|= disabled"))
test_or_assign ((const T*)0, tname, "|=");
if (rw_note (0 <= opt_assign [inx_shl_assign], 0, __LINE__,
"tests of operator<<= disabled"))
test_shr_assign ((const T*)0, tname, "<<=");
if (rw_note (0 <= opt_assign [inx_shr_assign], 0, __LINE__,
"tests of operator>>= disabled"))
test_shr_assign ((const T*)0, tname, ">>=");
}
/**************************************************************************/
static int
run_test (int, char**)
{
#undef TEST
#define TEST(T) test_op_assign ((const T*)0, #T)
TEST (int);
TEST (double);
#if 0x04020100 >= _RWSTD_VER
// test fails to compile with stdcxx 4.2.1 and prior due to
// STDCXX-512: http://issues.apache.org/jira/browse/STDCXX-512
rw_warn (0, 0, __LINE__,
"test of UserClass disabled in stdcxx 4.2.0 and prior "
"due to STDCXX-512");
#else // stdcxx >= 4.2.1
TEST (UserClass);
#endif // stdcxx version
return 0;
}
/**************************************************************************/
int main (int argc, char *argv[])
{
// FIXME: add command line options to enable/disable each operator
return rw_test (argc, argv, __FILE__,
"valarray.cassign",
0 /* no comment */,
run_test,
"|-times~ "
"|-divide~ "
"|-modulo~ "
"|-plus~ "
"|-minus~ "
"|-xor~ "
"|-and~ "
"|-or~ "
"|-shift_left~ "
"|-shift_right~",
opt_assign + inx_mul_assign,
opt_assign + inx_div_assign,
opt_assign + inx_mod_assign,
opt_assign + inx_add_assign,
opt_assign + inx_sub_assign,
opt_assign + inx_xor_assign,
opt_assign + inx_and_assign,
opt_assign + inx_or_assign,
opt_assign + inx_shl_assign,
opt_assign + inx_shr_assign,
(void*)0 /* sentinel */);
}