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

u/jbosch/DM-836 #3

Merged
merged 9 commits into from
Sep 25, 2014
1 change: 1 addition & 0 deletions include/lsst/afw/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
#include "lsst/afw/table/BaseColumnView.h"
#include "lsst/afw/table/FunctorKey.h"
#include "lsst/afw/table/aggregates.h"
#include "lsst/afw/table/arrays.h"

#endif // !LSST_AFW_table_h_INCLUDED
16 changes: 14 additions & 2 deletions include/lsst/afw/table/BaseRecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,21 @@ class BaseRecord
/**
* @brief Set a calculated or aggregate field.
*/
template <typename T>
void set(InputFunctorKey<T> const & key, T const & value) {
template <typename T, typename U>
void set(InputFunctorKey<T> const & key, U const & value) {
return key.set(*this, value);
}

template <typename Ref>
Ref operator[](ReferenceFunctorKey<Ref> const & key) {
return key.getReference(*this);
}

template <typename ConstRef>
ConstRef operator[](ConstReferenceFunctorKey<ConstRef> const & key) const {
return key.getConstReference(*this);
}

#endif // !SWIG

/// @brief Copy all field values from other to this, requiring that they have equal schemas.
Expand All @@ -163,6 +173,8 @@ class BaseRecord
/// @brief Copy field values from other to this, using a mapper.
void assign(BaseRecord const & other, SchemaMapper const & mapper);

ndarray::Manager::Ptr getManager() const { return _manager; }

virtual ~BaseRecord() { _table->_destroy(*this); }

protected:
Expand Down
62 changes: 62 additions & 0 deletions include/lsst/afw/table/FunctorKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@

namespace lsst { namespace afw { namespace table {

/**
* Base class for objects that can extract a value from a record, but are not a true Key themselves.
*
* Objects that inherit from OutputFunctorKey can be passed to BaseRecord::get(), just as true Keys can,
* but the record will simply pass itself to OutputFunctorKey::get() and return the result.
*/
template <typename T>
class OutputFunctorKey {
public:
Expand All @@ -38,6 +44,12 @@ class OutputFunctorKey {

};

/**
* Base class for objects that can set a value on a record, but are not a true Key themselves.
*
* Objects that inherit from InputFunctorKey can be passed to BaseRecord::set(), just as true Keys can,
* but the record will simply pass itself to OutputFunctorKey::set() along with the value that was passed.
*/
template <typename T>
class InputFunctorKey {
public:
Expand All @@ -48,9 +60,59 @@ class InputFunctorKey {

};

/**
* Convenience base class that combines the OutputFunctorKey and InputFunctorKey
*
* Most objects that can set a calculated or compound value from a record can also get that value back,
* so we provide this class to aggregate those interfaces.
*/
template <typename T>
class FunctorKey : public OutputFunctorKey<T>, public InputFunctorKey<T> {};

/**
* Base class for objects that can return a non-const reference to part of a record, but are not a true Key
*
* Objects that inherit from ReferenceFunctorKey can be passed to BaseRecord::operator[], just as true Keys
* can, but the record will simply pass itself to ReferenceFunctorKey::getReference().
*
* @note We'd combine this with the ConstReferenceFunctorKey interface if it weren't for the fact that
* we can't pass multiple template arguments to a Swig macro if either contains commas, and we'd need that
* to wrap a combined interface base class.
*/
template <typename T>
class ReferenceFunctorKey {
public:

#ifndef SWIG
virtual T getReference(BaseRecord & record) const = 0;
#endif

virtual ~ReferenceFunctorKey() {}

};

/**
* Base class for objects that can return a const reference to part of a record, but are not a true Key
*
* Objects that inherit from ConstReferenceFunctorKey can be passed to BaseRecord::operator[], just as true
* Keys can, but the record will simply pass itself to ReferenceFunctorKey::getConstReference().
*
* @note We'd combine this with the ReferenceFunctorKey interface if it weren't for the fact that
* we can't pass multiple template arguments to a Swig macro if either contains commas, and we'd need that
* to wrap a combined interface base class.
*/
template <typename T>
class ConstReferenceFunctorKey {
public:

#ifndef SWIG
virtual T getConstReference(BaseRecord const & record) const = 0;
#endif

virtual ~ConstReferenceFunctorKey() {}

};

}}} // namespace lsst::afw::table

#endif // !AFW_TABLE_FunctorKey_h_INCLUDED
8 changes: 4 additions & 4 deletions include/lsst/afw/table/aggregates.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ class PointKey : public FunctorKey< lsst::afw::geom::Point<T,2> > {
explicit PointKey(Key< Point<T> > const & other) : _x(other.getX()), _y(other.getY()) {}

/**
* @brief Construct from a subschema, assuming .x and .y subfields
* @brief Construct from a subschema, assuming x and y subfields
*
* If a schema has "a.x" and "a.y" fields, this constructor allows you to construct
* If a schema has "a_x" and "a_y" fields, this constructor allows you to construct
* a PointKey via:
* @code
* PointKey<T> k(schema["a"]);
Expand Down Expand Up @@ -127,9 +127,9 @@ class QuadrupoleKey : public FunctorKey< lsst::afw::geom::ellipses::Quadrupole >
{}

/**
* @brief Construct from a subschema, assuming .xx, .yy, and .xy subfields
* @brief Construct from a subschema, assuming xx, yy, and xy subfields
*
* If a schema has "a.xx", "a.yy", and "a.xy" fields, this constructor allows you to construct
* If a schema has "a_xx", "a_yy", and "a_xy" fields, this constructor allows you to construct
* a QuadrupoleKey via:
* @code
* QuadrupoleKey k(schema["a"]);
Expand Down
154 changes: 154 additions & 0 deletions include/lsst/afw/table/arrays.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// -*- lsst-c++ -*-
/*
* LSST Data Management System
* Copyright 2008-2014 LSST Corporation.
*
* This product includes software developed by the
* LSST Project (http://www.lsst.org/).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the LSST License Statement and
* the GNU General Public License along with this program. If not,
* see <http://www.lsstcorp.org/LegalNotices/>.
*/
#ifndef AFW_TABLE_arrays_h_INCLUDED
#define AFW_TABLE_arrays_h_INCLUDED

#include "lsst/afw/table/FunctorKey.h"
#include "lsst/afw/table/Schema.h"

namespace lsst { namespace afw { namespace table {

/**
* @brief A FunctorKey used to get or set a ndarray::Array from a sequence of scalar Keys.
*
* ArrayKey operates on the convention that arrays are defined by a set of contiguous scalar fields
* (i.e. added to the Schema in order, with no interruption) of the same type, with a common field
* name prefix and "_0", "_1" etc. suffixes.
*/
template <typename T>
class ArrayKey : public FunctorKey< ndarray::Array<T const,1,1> >,
public ReferenceFunctorKey< ndarray::ArrayRef<T,1,1> >,
public ConstReferenceFunctorKey< ndarray::ArrayRef<T const,1,1> >
{
public:

/**
* Add an array of fields to a Schema, and return an ArrayKey that points to them.
*
* @param[in,out] schema Schema to add fields to.
* @param[in] name Name prefix for all fields; "_0", "_1", etc. will be appended to this
* to form the full field names.
* @param[in] doc String used as the documentation for the fields. Should include a single
* boost::format template string, which will be substituted with the
* appropriate element from the docData array to form the full documentation
* string.
* @param[in] unit String used as the unit for all fields.
* @param[in] docData Vector of values substituted into the doc fields. The length of the vector
* determines the number of fields added.
*/
static ArrayKey addFields(
Schema & schema,
std::string const & name,
std::string const & doc,
std::string const & unit,
std::vector<T> const & docData
);

/**
* Add an array of fields to a Schema, and return an ArrayKey that points to them.
*
* @param[in,out] schema Schema to add fields to.
* @param[in] name Name prefix for all fields; "_0", "_1", etc. will be appended to this
* to form the full field names.
* @param[in] doc String used as the documentation for the fields.
* @param[in] unit String used as the unit for all fields.
* @param[in] size Number of fields to add.
*/
static ArrayKey addFields(
Schema & schema,
std::string const & name,
std::string const & doc,
std::string const & unit,
int size
);

/// Default constructor; instance will not be usuable unless subsequently assigned to.
ArrayKey() : _begin() {}

/// Construct from a vector of scalar Keys
explicit ArrayKey(std::vector< Key<T> > const & keys);

/**
* Construct from a compound Key< Array<T> >
*
* Key< Array<T> > is now deprecated in favor of ArrayKey; this factory function is intended to
* aid in the transition.
*/
explicit ArrayKey(Key< Array<T> > const & other);

/**
* @brief Construct from a subschema, assuming *_0, *_1, *_2, etc. subfields
*
* If a schema has "a_0", "a_1", and "a_2" fields, this constructor allows you to construct
* a 3-element ArrayKey via:
* @code
* ArrayKey<T> k(schema["a"]);
* @endcode
*/
ArrayKey(SubSchema const & s);

/// Return the number of elements in the array.
int getSize() const { return _size; }

/// Get an array from the given record
virtual ndarray::Array<T const,1,1> get(BaseRecord const & record) const;

/// Set an array in the given record
virtual void set(BaseRecord & record, ndarray::Array<T const,1,1> const & value) const;

#ifndef SWIG
/// Get non-const reference array from the given record
virtual ndarray::ArrayRef<T,1,1> getReference(BaseRecord & record) const;

/// Get const reference array from the given record
virtual ndarray::ArrayRef<T const,1,1> getConstReference(BaseRecord const & record) const;
#endif

//@{
/// Compare the FunctorKey for equality with another, using the underlying scalar Keys
bool operator==(ArrayKey<T> const & other) const {
return other._begin == _begin && other._size == _size;
}
bool operator!=(ArrayKey<T> const & other) const { return !operator==(other); }
//@}

/// Return True if the FunctorKey contains valid scalar keys.
bool isValid() const { return _begin.isValid(); }

/// Return a scalar Key for an element of the array
Key<T> operator[](int i) const;

/// @brief Return a FunctorKey corresponding to a range of elements
ArrayKey slice(int begin, int end) const;

private:

ArrayKey(Key<T> const & begin, int size) : _begin(begin), _size(size) {}

Key<T> _begin;
int _size;
};

}}} // namespace lsst::afw::table

#endif // !AFW_TABLE_arrays_h_INCLUDED
6 changes: 6 additions & 0 deletions include/lsst/afw/table/detail/Access.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ class Access {
);
}

/// @internal @brief Access to the private Key constructor.
template <typename T>
static Key<T> makeKey(int offset) {
return Key<T>(offset);
}

/// @internal @brief Access to the private Key constructor.
template <typename T>
static Key<T> makeKey(Field<T> const & field, int offset) {
Expand Down
26 changes: 22 additions & 4 deletions python/lsst/afw/table/Base.i
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,18 @@ def asKey(self):
%template(FunctorKey ## PYNAME) lsst::afw::table::FunctorKey< U >;
%enddef

%define %declareReferenceFunctorKey(PYNAME, U...)
%shared_ptr(lsst::afw::table::ReferenceFunctorKey< U >);
%nodefaultctor lsst::afw::table::ReferenceFunctorKey< U >;
%template(ReferenceFunctorKey ## PYNAME) lsst::afw::table::ReferenceFunctorKey< U >;
%enddef

%define %declareConstReferenceFunctorKey(PYNAME, U...)
%shared_ptr(lsst::afw::table::ConstReferenceFunctorKey< U >);
%nodefaultctor lsst::afw::table::ConstReferenceFunctorKey< U >;
%template(ConstReferenceFunctorKey ## PYNAME) lsst::afw::table::ConstReferenceFunctorKey< U >;
%enddef

// =============== BaseTable and BaseRecord =================================================================

%shared_ptr(lsst::afw::table::BaseTable);
Expand All @@ -431,16 +443,22 @@ def asKey(self):
def cast(self, type_):
return type_._cast(self)
%}
// Allow field name strings be used in place of keys (but only in Python)
%pythonprepend __getitem__ %{
if isinstance(args[0], basestring):
return self[self.schema.find(args[0]).key]
%feature("shadow") __getitem__ %{
def __getitem__(self, key):
if isinstance(key, basestring):
return self[self.schema.find(key).key]
try:
return $action(self, key)
except NotImplementedError:
# If this doesn't work as a regular key, try it as a FunctorKey
return key.get(self)
%}
%pythonprepend __setitem__ %{
if isinstance(args[0], basestring):
self[self.schema.find(args[0]).key] = args[1]
return
%}
// Allow field name strings be used in place of keys (but only in Python)
%feature("shadow") get %{
def get(self, key):
if isinstance(key, basestring):
Expand Down