Re: sc_vector proposal

From: <john.aynsley@doulos.com>
Date: Wed Nov 10 2010 - 05:20:50 PST

Philipp, Andy,

    my_vec_of_modules2.init(N, sc_bind( &M::my_module_creator_func, this,
sc_unamed::_1, sc_unamed::_2 ) );

works.

Do we need sc_unamed added to the LRM? Do we want to change it to
sc_unnamed? Or to something else? Does anything else need adding from the
revised boost stuff in SystemC?

John A

From:
"Philipp A. Hartmann" <philipp.hartmann@offis.de>
To:
john.aynsley@doulos.com
Cc:
systemc-p1666-technical@eda.org
Date:
09/11/2010 18:36
Subject:
Re: sc_vector proposal

John,

thanks for your comments and your nice review of the proposal.
Please find my answers to your questions embedded below.

On 09/11/10 14:06, john.aynsley@doulos.com wrote:

[snip basic feature description]

> Then we add a few wrinkles. It is possible to bind a vector-of-ports to
a
> vector-of-channels directly, with no explicit loop, e.g.
>
> module_inst->ports.bind( sigs ); // ports is a vector-of-ports
> and sigs is a vector-of-sigs
>
> Very nice. It is also possible to bind just a subset of the
> vector-of-ports, then come back for another pass later:
>
> typedef sc_vector<sc_inout<int> > port_type;
> port_type::iterator it;
>
> // Bind upper half of ports vector to hi_sigs
> it = module_inst->ports.bind( hi_sigs.begin(), hi_sigs.end() );
//
> hi_sigs.size() < ports.size()
>
> // Bind lower half of ports vector to lo_sigs
> it = module_inst->ports.bind( lo_sigs.begin(), lo_sigs.end(),
it);
> // Notice binding starts from position 'it' within ports vector
>
> The above syntax - having bind return the position of the 1st unbound
> element - seems a little arcane to me. Are we all okay with it?

Returning the "last" iterator follows the approach known from some of
the iterator-based STL algorithms, like std::copy or std::transform.
To me, this is the simplest way to define the interface. But I'm free
to any suggestions in that area.

Note, that the user can of course ignore the return value of the bind
function and use an explicit iterator for the second bind offset:

  module_inst->ports.bind( lo_sigs.begin(), lo_sigs.end()
                         , module_inst.begin() + hi_sigs.size() );

If we feel, that this iterator-based interface is too complex to
understand or describe, we can also drop it from the standard. Manually
looping over parts of the vectors is still possible and maybe easier to
explain:

  port_type it = module_inst.ports.begin();

  for( int i=0; i<hi_sigs.size(); ++i, ++it );
    it->bind( hi_sigs[i] );

  for( int i=0; i<lo_sigs.size(); ++i, ++it );
    it->bind( lo_sigs[i] );

The returned iterator from the bind() calls can also be used to check,
if all elements have been bound:

  sc_assert( it == module_inst->ports.end() ); // all bound

> But sometimes, rather than having a vector-of-ports, you might have a
> vector-of-modules each containing a single port. In this case it is
> possible to treat these ports distributed across multiple modules as one

> vector, using sc_view. For example:
>
> struct Initiator1: sc_module
> {
> sc_port<i_f> port;
> ...
> struct Target: public sc_module, private i_f
> {
> sc_export<i_f> xp;
> ...
>
> sc_vector<Initiator1> initiator_vec;
> sc_vector<Target> target_vec;
>
> sc_view(initiator_vec, &Initiator1::port).bind( sc_view
> (target_vec, &Target::xp) );
>
> In the above
> sc_view(initiator_vec, &Initiator1::port) creates a
> vector-of-ports
> sc_view(target_vec, &Target::xp) creates a vector-of-exports
> .bind() does a vector-to-vector bind
>
> The semantics are very nice, but I am not particularly happy with the
name
> sc_view, because this is not suggestive of what is actually happening,
> that is, gathering together a vector from a set of elements distributed
> across another vector. Is there a precedent for the name sc_view,
Phillip?
> How about something like sc_gather or sc_make_vector or sc_vectorize
> instead?

I don't have a strong feeling about the name sc_view. Especially, since
I'm not particularly talented in given well-descriptive names.

  The chosen name sc_view originates from the database world, where a
view is some kind of a "stored query", which can then be manipulated
more easily. See e.g. http://en.wikipedia.org/wiki/Database_view

  The cause is more or less the same here: With sc_view, you pre-select
a given member of the vector, without actually creating a "real"
sc_vector object. Only accesses via this "view" are forwarded to the
_member object_ of the underlying vector element, instead of the element
itself.

I could think of other names like
  sc_member[_view]()
  sc_children()
  sc_select[ion]()

But as I said, I'm not really good at this. ;-)

[snip get_elements]

> Finally, in the special case of a vector-of-modules, it is possible to
> pass extra arguments to the module constructor, e.g.
>
> struct my_module: sc_module
> {
> my_module(sc_module_name n, string weird_arg ) {...}
> ...
> ...
> sc_vector<my_module> my_vec;
>
> struct my_module_creator
> {
> my_module_creator( string arg ) : weird_arg(arg) {}
>
> my_module* operator() (const char* name, size_t)
> {
> return new my_module(name, weird_arg );
> }
> string weird_arg;
> };
> ...
> my_vec.init(N, my_module_creator("The Creator"));
>
> This works, but creating the function object feels like having to jump
> through hoops. I think regular users are going to find it confusing. Can

> we do any clever tricks to make it easier to use?

The reason for the complexity is two-fold:

 - The Creator needs to receive the designated name for the element
   (and, for convenience, the current index)
 - The Creator may need to have a local state e.g. to store
   or generate additional arguments.

  To use a custom Creator, the easiest/best solution depends on the
current situation. If you have some globally accessible way to chose
your additional arguments, you may get away with a plain function:

my_module* my_module_creator_func( const char* name, size_t i )
{
  return new my_module( name, WEIRD_ARG );
}
my_vec.init(N, my_module_creator_func );

  Another quite simple way is to define a local member function in the
surrounding module and use sc_bind for the creator. This way, you can
access the whole state of the owning module during creation and do all
kinds of nice stuff, like binding transport functions etc:

SC_MODULE(parent)
{
  sc_vector< my_module > my_vec;
  std::string weird_arg;

  // member function as creator to use with sc_bind()
  my_module* init_element( const char* n, unsigned i )
  {
    my_module * mod = new my_module( n, weird_arg );
    mod->register_invalidate_direct_mem_ptr( this,
             &parent::invalidate_direct_mem_ptr, i );
    return mod;
  }

  SC_CTOR(parent)
  {
    // use sc_bind, "this" + placeholders as creator
    // note: you may need to use sc_unamed::_1 in SystemC 2.3

    my_vec.init( N, sc_bind( &parent::init_element, this, _1, _2 ) );
  }
};

To me, the above is as easy as it can get in current C++.
Of course, C++0x lambdas will rock here in the future:

    std::string weird_arg;
    my_vec.init( N,
                [&]( const char*, size_t i)
                   { return new my_module( n, weird_arg ); }
               );

I'm of course interested in any simpler approach to custom element
creation. But I've spent some time on this issue and could not come up
with any easier solution for supporting arbitrary constructor signatures.

  I can see that this is not really part of C++/SystemC 101, but at
least it's consistent with other functor APIs in C++, like Compare (in
std::map and std::sort) or Function in std::for_each.

Greetings from Oldenburg,
Philipp

-- 
Philipp A. Hartmann
Hardware/Software Design Methodology Group
OFFIS Institute for Information Technology
R&D Division Transportation · FuE-Bereich Verkehr
Escherweg 2 · 26121 Oldenburg · Germany
Phone/Fax: +49-441-9722-420/282 · PGP: 0x9161A5C0 · http://www.offis.de/
-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
Received on Wed Nov 10 05:21:16 2010

This archive was generated by hypermail 2.1.8 : Wed Nov 10 2010 - 05:21:18 PST