Showing posts with label Templates. Show all posts
Showing posts with label Templates. Show all posts

Sunday, October 13, 2019

STLSoft 1.10.1 beta-16 : added stlsoft::count_bits(int)

In the recent 1.10.1-beta16 release of STLSoft, one of the changes was a minor enhancement to the stlsoft::count_bits() function suite, to make working with enumerations simpler.

The prior release(s) contained the following overloads of stlsoft::count_bits():

unsigned
count_bits(
  uint32_t v
) /* noexcept */
;

unsigned
count_bits(
  uint64_t v
) /* noexcept */
;

Pretty discoverable - either pass a 32-bit or a 64-bit unsigned integer. All the use-cases up to now for which I've needed this function have involved one of those types.

However, a new, and probably widely-applicable, use-case popped up. I have a CLI Windows program pown that prints the ownership of a file/directory. It recognises a number of CLI flags, some of which are mutually exclusive, and combines them into a flags parameter (of type int)  that is then passed into the program logic.

Some of the mutually-exclusive flag pertain to how the file path should be shown:


CLI flag Enumerator
'--show-file-rel-path' POWN_F_SHOW_FILE_REL_PATH (0x0010)
'--show-file-path' POWN_F_SHOW_FILE_PATH (0x0020)
'--show-file-stem' POWN_F_SHOW_FILE_STEM (0x0040)

POWN_F_SHOW_FILE_MASK_ (0x00f0)

The last enumerator, POWN_F_SHOW_FILE_MASK_,  is a mask for the show-file flags. In order to verify that, at most, a single relevant flag had been specified was code such as the following:

if (stlsoft::count_bits(flags & POWN_F_SHOW_FILE_MASK_) > 1)
{
  . . . // issue contingent report, and ...

  return EXIT_FAILURE; // ... exit the program
}

Neither of the two extant overloads was a best fit, to the compiler said no.

What I needed was an overload that takes an int. But which overload should it invoke?

I could just cast an int to  a uint64_t, but I didn't want to risk any performance costs in code generation in any of the many (versions of) compilers that are supported. Instead, it felt like an opportunity for some, admittedly pretty simple, template metaprogramming.

Without further ado, here's the implementation (with documentation and other stuff elided for brevity):


#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION

template <size_t N_bytes>


unsigned
count_bits_int_(
  int v
);

template <>
unsigned
count_bits_int_<4>(
  int v
)
{
  return count_bits(static_cast(v));
}

template <>
unsigned
count_bits_int_<8>(
  int v
)
{
  return count_bits(static_cast(v));
}
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */

unsigned
count_bits(
  int v
)
{
  return count_bits_int_<sizeof(int)>(v);
}


Friday, January 2, 2009

Working with other libraries, part 3: use consistent conventions for member types

Ever get confused by the names of member variables/types/functions of C++ class templates?

Boost.Format's basic_format class template defines five public and two private member types, using a mix of four different naming conventions!

[an extract from boost/format/format_class.hpp]

// in namespace boost
template <class Ch, class Tr, class Alloc>
class basic_format
{
private:
  typedef typename io::CompatTraits<Tr>::compatible_type compat_traits;
  typedef io::detail::stream_format_state<Ch, Tr> stream_format_state;
  
public:
  typedef Ch CharT;
  typedef std::basic_string<Ch, Tr, Alloc> string_type;
  typedef typename string_type::size_type size_type;
  typedef io::detail::format_item<Ch, Tr, Alloc> format_item_t;
  typedef io::basic_altstringbuf<Ch, Tr, Alloc> internal_streambuf_t;

In Extended STL, volume 1: Collections and Iterators I recommend the use of the following naming convention for member types:
  1. For public member types that share names with standard components that have the same logical purpose, follow the standard convention and use the standard name. An example would be iterator.
  2. For public member types that are not covered by clause 1, use the _type suffix. An example would be char_type.
  3. For private member types, use the _type_ suffix. An example would be compat_traits_type_.
and
  1. For public API (non-member) types use the _t suffix. An example would be pan_char_t.
  2. For non-public/implementation API (non-member) types use the _t_ suffix. An example would be b64ErrorString_t_.


I've been using this for many years without a blip. I can read my code, even some years later, and understand what's a type and what isn't and also, importantly, which types are for consumption in the outside world and which are internal to the component. Q.E.D.