Eigen unsupported Tensor to Eigen matrix - eigen3

I get a Eigen::Tensor<std::complex, 2> after some operations on a Tensor with more dimensions. Is there an easy way to create a Eigen::MatrixXcf from this Tensor-object or do I have to copy the values manually?

Original answer. See the update further down.
I am currently using Eigen version 3.3.4, and there is no easy built-in function to cast between Eigen::Tensor types and the more familiar Matrix or Array types. It would have been handy to have something like the .matrix() or .array() methods.
There is also no easy way to add such methods via the plugin mechanism, because the Tensor module doesn't seem to support plugins. If anybody knows how please comment.
In the meantime it is possible to make make fairly efficient workarounds using the Map functions. The following works in C++14, for casting Tensors to and from Matrices
#include <unsupported/Eigen/CXX11/Tensor>
#include <iostream>
template<typename T>
using MatrixType = Eigen::Matrix<T,Eigen::Dynamic, Eigen::Dynamic>;
template<typename Scalar,int rank, typename sizeType>
auto Tensor_to_Matrix(const Eigen::Tensor<Scalar,rank> &tensor,const sizeType rows,const sizeType cols)
{
return Eigen::Map<const MatrixType<Scalar>> (tensor.data(), rows,cols);
}
template<typename Scalar, typename... Dims>
auto Matrix_to_Tensor(const MatrixType<Scalar> &matrix, Dims... dims)
{
constexpr int rank = sizeof... (Dims);
return Eigen::TensorMap<Eigen::Tensor<const Scalar, rank>>(matrix.data(), {dims...});
}
int main () {
Eigen::Tensor<double,4> my_rank4 (2,2,2,2);
my_rank4.setRandom();
Eigen::MatrixXd mymatrix = Tensor_to_Matrix(my_rank4, 4,4);
Eigen::Tensor<double,3> my_rank3 = Matrix_to_Tensor(mymatrix, 2,2,4);
std::cout << my_rank3 << std::endl;
return 0;
}
This works with complex types just as well.
Unfortunately these functions only take tensors, not tensor operations. For instance, this doesn't work:
Eigen::MatrixXd mymatrix = Tensor_to_Matrix(my_rank4.shuffle(Eigen::array<long,4>{1,0,3,2}), 4,4);
Update August 2021:
A memory leak was fixed, see the code below. In short, one must remember to call .cleanup() member function on the Eigen::TensorEvaluator object to deallocate internal temporary buffers.
Update October 2021:
Since I submitted the answer above back in 2018, I have learned how to map and cast between Eigen::Tensor and Eigen::Matrix (and expressions thereof) with a minimal amount of temporaries.
I have used the helper functions below since Eigen 3.3.7 using C++17
#include <unsupported/Eigen/CXX11/Tensor>
#include <iostream>
template<typename T>
using MatrixType = Eigen::Matrix<T,Eigen::Dynamic, Eigen::Dynamic>;
template<typename T>
using VectorType = Eigen::Matrix<T,Eigen::Dynamic, 1>;
/*
*
* Convert Eigen::Tensor --> Eigen::Matrix
*
*/
template<typename Derived, typename Device = Eigen::DefaultDevice>
class selfCleaningEvaluator {
private:
using Evaluator = Eigen::TensorEvaluator<const Eigen::TensorForcedEvalOp<const Derived>, Device>;
Evaluator m_eval;
public:
selfCleaningEvaluator(const Evaluator &eval) : m_eval(eval) {}
selfCleaningEvaluator(const Eigen::TensorBase<Derived, Eigen::ReadOnlyAccessors> &expr, const Device &device = Device())
: m_eval(Evaluator(expr.eval(), device)) {
m_eval.evalSubExprsIfNeeded(nullptr);
}
~selfCleaningEvaluator() {
// This whole point of this object is to call cleanup automatically on destruct.
// If there are pending operations to evaluate, m_eval will allocate a buffer to hold a result,
// which needs to be deallocated.
m_eval.cleanup();
}
constexpr auto rank() {
using DimType = typename decltype(m_eval)::Dimensions::Base;
return DimType{}.size(); // Because Derived::Dimensions is sometimes wrong
}
const Evaluator *operator->() const { return &m_eval; }
Evaluator *operator->() { return &m_eval; }
constexpr auto map() {
// We inspect m_eval to get the type, rank and dimensions, because it has the resulting tensor,
// whereas Derived is the tensor type _before_ whatever operation is pending (if any).
using DimType = typename decltype(m_eval)::Dimensions::Base;
constexpr auto rank = DimType{}.size();
using Scalar = typename Eigen::internal::remove_const<typename decltype(m_eval)::Scalar>::type;
return Eigen::TensorMap<Eigen::Tensor<Scalar, rank>>(m_eval.data(), m_eval.dimensions());
}
};
// Evaluates expressions if needed
template<typename T, typename Device = Eigen::DefaultDevice>
auto asEval(const Eigen::TensorBase<T, Eigen::ReadOnlyAccessors> &expr, // An Eigen::TensorBase object (Tensor, TensorMap, TensorExpr... )
const Device &device = Device()) { // Override to evaluate on another device, e.g. thread pool or gpu.
return selfCleaningEvaluator(expr, device);
}
// Converts any Eigen::Tensor (or expression) to an Eigen::Matrix with shape rows/cols
template<typename T, typename sizeType, typename Device = Eigen::DefaultDevice>
auto MatrixCast(const Eigen::TensorBase<T, Eigen::ReadOnlyAccessors> &expr, const sizeType rows, const sizeType cols, const Device &device = Device()) {
auto tensor = asEval(expr, device);
auto tensorMap = tensor.map();
using Scalar = typename decltype(tensorMap)::Scalar;
return static_cast<MatrixType<Scalar>>(Eigen::Map<const MatrixType<Scalar>>(tensorMap.data(), rows, cols));
}
// Converts any Eigen::Tensor (or expression) to an Eigen::Matrix with shape rows/cols
template<typename T, typename Device = Eigen::DefaultDevice>
auto VectorCast(const Eigen::TensorBase<T, Eigen::ReadOnlyAccessors> &expr, const Device &device = Device()) {
auto tensor = asEval(expr, device);
auto tensorMap = tensor.map();
auto size = Eigen::internal::array_prod(tensorMap.dimensions());
using Scalar = typename decltype(tensorMap)::Scalar;
return static_cast<VectorType<Scalar>>(Eigen::Map<const VectorType<Scalar>>(tensorMap.data(), size));
}
// View an existing Eigen::Tensor as an Eigen::Map<Eigen::Matrix>
template<typename Scalar, auto rank, typename sizeType>
auto MatrixMap(const Eigen::Tensor<Scalar, rank> &tensor, const sizeType rows, const sizeType cols) {
return Eigen::Map<const MatrixType<Scalar>>(tensor.data(), rows, cols);
}
// View an existing Eigen::Tensor of rank 2 as an Eigen::Map<Eigen::Matrix>
// Rows/Cols are determined from the matrix
template<typename Scalar>
auto MatrixMap(const Eigen::Tensor<Scalar, 2> &tensor) {
return Eigen::Map<const MatrixType<Scalar>>(tensor.data(), tensor.dimension(0), tensor.dimension(1));
}
// View an existing Eigen::Tensor of rank 1 as an Eigen::Map<Eigen::Vector>
// Rows is the same as the size of the tensor.
template<typename Scalar, auto rank>
auto VectorMap(const Eigen::Tensor<Scalar, rank> &tensor) {
return Eigen::Map<const VectorType<Scalar>>(tensor.data(), tensor.size());
}
/*
*
* Convert Eigen::Matrix --> Eigen::Tensor
*
*/
// Converts an Eigen::Matrix (or expression) to Eigen::Tensor
// with dimensions specified in std::array
template<typename Derived, typename T, auto rank>
Eigen::Tensor<typename Derived::Scalar, rank>
TensorCast(const Eigen::EigenBase<Derived> &matrix, const std::array<T, rank> &dims) {
return Eigen::TensorMap<const Eigen::Tensor<const typename Derived::Scalar, rank>>
(matrix.derived().eval().data(), dims);
}
// Converts an Eigen::Matrix (or expression) to Eigen::Tensor
// with dimensions specified in Eigen::DSizes
template<typename Derived, typename T, auto rank>
Eigen::Tensor<typename Derived::Scalar, rank>
TensorCast(const Eigen::EigenBase<Derived> &matrix, const Eigen::DSizes<T, rank> &dims) {
return Eigen::TensorMap<const Eigen::Tensor<const typename Derived::Scalar, rank>>
(matrix.derived().eval().data(), dims);
}
// Converts an Eigen::Matrix (or expression) to Eigen::Tensor
// with dimensions as variadic arguments
template<typename Derived, typename... Dims>
auto TensorCast(const Eigen::EigenBase<Derived> &matrix, const Dims... dims) {
static_assert(sizeof...(Dims) > 0, "TensorCast: sizeof... (Dims) must be larger than 0");
return TensorCast(matrix, std::array<Eigen::Index, sizeof...(Dims)>{dims...});
}
// Converts an Eigen::Matrix (or expression) to Eigen::Tensor
// with dimensions directly as arguments in a variadic template
template<typename Derived>
auto TensorCast(const Eigen::EigenBase<Derived> &matrix) {
if constexpr(Derived::ColsAtCompileTime == 1 or Derived::RowsAtCompileTime == 1) {
return TensorCast(matrix, matrix.size());
} else {
return TensorCast(matrix, matrix.rows(), matrix.cols());
}
}
// View an existing Eigen::Matrix as Eigen::TensorMap
// with dimensions specified in std::array
template<typename Derived, auto rank>
auto TensorMap(const Eigen::PlainObjectBase<Derived> &matrix, const std::array<long, rank> &dims) {
return Eigen::TensorMap<const Eigen::Tensor<const typename Derived::Scalar, rank>>(matrix.derived().data(), dims);
}
// View an existing Eigen::Matrix as Eigen::TensorMap
// with dimensions as variadic arguments
template<typename Derived, typename... Dims>
auto TensorMap(const Eigen::PlainObjectBase<Derived> &matrix, const Dims... dims) {
return TensorMap(matrix, std::array<long, static_cast<int>(sizeof...(Dims))>{dims...});
}
// View an existing Eigen::Matrix as Eigen::TensorMap
// with dimensions determined automatically from the given matrix
template<typename Derived>
auto TensorMap(const Eigen::PlainObjectBase<Derived> &matrix) {
if constexpr(Derived::ColsAtCompileTime == 1 or Derived::RowsAtCompileTime == 1) {
return TensorMap(matrix, matrix.size());
} else {
return TensorMap(matrix, matrix.rows(), matrix.cols());
}
}
int main () {
Eigen::Tensor<double,4> my_rank4 (2,2,2,2);
my_rank4.setRandom();
Eigen::MatrixXd mymatrix = MatrixCast(my_rank4, 4,4); // Cast Eigen::Tensor --> Eigen::Matrix
Eigen::Tensor<double,3> my_rank3 = TensorCast(mymatrix, 2,2,4); // Cast Eigen::Matrix --> Eigen::Tensor
std::cout << my_rank3 << std::endl;
return 0;
}
Try on compiler-explorer

Related

Multi-threaded array initialization in Kotlin

Say, I have the following functions that allow me to create matrices:
inline fun <reified T> Matrix2D(w: Int, h: Int, init: (Int, Int) -> T) =
Array(w){ x -> Array(h){ y -> init(x, y) } }
inline fun <reified T> Matrix2D(w: Int, h: Int, value: T) =
Array(w){ Array(h){ value } }
// For example:
val m = Matrix2D(400, 400) { x, y ->
Color(x.toDouble() / 400.0, y.toDouble() / 400.0, 0.0)
}
I also don't want to allow for nullability, cause it's going to be a pain to deal with element access later.
How would I initialize such a matrix using multiple threads/coroutines, where each one initializes its own tile of the matrix, without allowing for nullability of the matrix cells?
Yes, you can create a Matrix2D in parallel, for example:
val threads = Matrix2D(10, 3){x, y-> Thread.currentThread() }.flatten().distinct()
// v--- size > 1
println(threads.size)
inline fun <reified T> Matrix2D(w: Int, h: Int, crossinline init: (Int, Int) -> T)=
// v--- create array in parallel
generatorOf(w){x -> Array(h) {y -> init(x, y) } }.map{ it() }.toTypedArray()
// get the final result `Matrix2D` ---^
// v--- create array generator
inline fun <reified T> generatorOf(size: Int, crossinline init:(Int) -> T) =
//v--- you can use `ForkJoinPool#commonPool`, but it will run task in work thread
ForkJoinPool(20).let {
Array(size) { i -> it.submit(Callable { init(i) }).let { { it.get() } } }
// return the lambda generator ()->T ---^
}
You also can write an extension function toArray to remove the middle map operation & promote the performance, for example:
inline fun <reified T> Matrix2D(w: Int, h: Int, crossinline init: (Int, Int) -> T)=
// v--- init array elements in parallel
generatorOf(w) { x -> Array(h) { y -> init(x, y) } }.toArray{it()}
// transform to the `Matrix2D` directly ---^
// v--- convert Array<T> to Array<R>
inline fun <T,reified R> Array<T>.toArray(transform:(T)->R):Array<R>{
return Array(size){i->transform(get(i))}
}
Why did the Matrix2D start threads in outer array initialization, you can see sum two double[][] with parallel stream as further.

S4 object with a pointer to a C struct

I have a third-party C library I am using to write an R extension. I am required to create a few structs defined in the library (and initialize them) I need to maintain them as part of an S4 object (think of these structs as defining to state of a computation, to destroy them would be to destroy all remaining computation and the results of all that has been already computed).
I am thinking of creating a S4 object to hold pointers these structs as void* pointers but it is not at all clear how to do so, what would be the type of the slot?
As pointed out by #hrbrmstr, you can use the externalptr type to keep such objects "alive", which is touched on in this section of Writing R Extensions, although I don't see any reason why you will need to store anything as void*. If you don't have any issue with using a little C++, the Rcpp class XPtr can eliminate a fair amount of the boilerplate involved with managing EXTPTRSXPs. As an example, assume the following simplified example represents your third party library's API:
#include <Rcpp.h>
#include <stdlib.h>
typedef struct {
unsigned int count;
double total;
} CStruct;
CStruct* init_CStruct() {
return (CStruct*)::malloc(sizeof(CStruct));
}
void free_CStruct(CStruct* ptr) {
::free(ptr);
::printf("free_CStruct called.\n");
}
typedef Rcpp::XPtr<CStruct, Rcpp::PreserveStorage, free_CStruct> xptr_t;
When working with pointers created via new it is generally sufficient to use Rcpp::XPtr<SomeClass>, because the default finalizer simply calls delete on the held object. However, since you are dealing with a C API, we have to supply the (default) template parameter Rcpp::PreserveStorage, and more importantly, the appropriate finalizer (free_CStruct in this example) so that the XPtr does not call delete on memory allocated via malloc, etc., when the corresponding R object is garbage collected.
Continuing with the example, assume you write the following functions to interact with your CStruct:
// [[Rcpp::export]]
xptr_t MakeCStruct() {
CStruct* ptr = init_CStruct();
ptr->count = 0;
ptr->total = 0;
return xptr_t(ptr, true);
}
// [[Rcpp::export]]
void UpdateCStruct(xptr_t ptr, SEXP x) {
if (TYPEOF(x) == REALSXP) {
R_xlen_t i = 0, sz = XLENGTH(x);
for ( ; i < sz; i++) {
if (!ISNA(REAL(x)[i])) {
ptr->count++;
ptr->total += REAL(x)[i];
}
}
return;
}
if (TYPEOF(x) == INTSXP) {
R_xlen_t i = 0, sz = XLENGTH(x);
for ( ; i < sz; i++) {
if (!ISNA(INTEGER(x)[i])) {
ptr->count++;
ptr->total += INTEGER(x)[i];
}
}
return;
}
Rf_warning("Invalid SEXPTYPE.\n");
}
// [[Rcpp::export]]
void SummarizeCStruct(xptr_t ptr) {
::printf(
"count: %d\ntotal: %f\naverage: %f\n",
ptr->count, ptr->total,
ptr->count > 0 ? ptr->total / ptr->count : 0
);
}
// [[Rcpp::export]]
int GetCStructCount(xptr_t ptr) {
return ptr->count;
}
// [[Rcpp::export]]
double GetCStructTotal(xptr_t ptr) {
return ptr->total;
}
// [[Rcpp::export]]
void ResetCStruct(xptr_t ptr) {
ptr->count = 0;
ptr->total = 0.0;
}
At this point, you have done enough to start handling CStructs from R:
ptr <- MakeCStruct() will initialize a CStruct and store it as an externalptr in R
UpdateCStruct(ptr, x) will modify the data stored in the CStruct, SummarizeCStruct(ptr) will print a summary, etc.
rm(ptr); gc() will remove the ptr object and force the garbage collector to run, thus calling free_CStruct(ptr) and destroying the object on the C side of things as well
You mentioned the use of S4 classes, which is one option for containing all of these functions in a single place. Here's one possibility:
setClass(
"CStruct",
slots = c(
ptr = "externalptr",
update = "function",
summarize = "function",
get_count = "function",
get_total = "function",
reset = "function"
)
)
setMethod(
"initialize",
"CStruct",
function(.Object) {
.Object#ptr <- MakeCStruct()
.Object#update <- function(x) {
UpdateCStruct(.Object#ptr, x)
}
.Object#summarize <- function() {
SummarizeCStruct(.Object#ptr)
}
.Object#get_count <- function() {
GetCStructCount(.Object#ptr)
}
.Object#get_total <- function() {
GetCStructTotal(.Object#ptr)
}
.Object#reset <- function() {
ResetCStruct(.Object#ptr)
}
.Object
}
)
Then, we can work with the CStructs like this:
ptr <- new("CStruct")
ptr#summarize()
# count: 0
# total: 0.000000
# average: 0.000000
set.seed(123)
ptr#update(rnorm(100))
ptr#summarize()
# count: 100
# total: 9.040591
# average: 0.090406
ptr#update(rnorm(100))
ptr#summarize()
# count: 200
# total: -1.714089
# average: -0.008570
ptr#reset()
ptr#summarize()
# count: 0
# total: 0.000000
# average: 0.000000
rm(ptr); gc()
# free_CStruct called.
# used (Mb) gc trigger (Mb) max used (Mb)
# Ncells 484713 25.9 940480 50.3 601634 32.2
# Vcells 934299 7.2 1650153 12.6 1308457 10.0
Of course, another option is to use Rcpp Modules, which more or less take care of the class definition boilerplate on the R side (using reference classes rather than S4 classes, however).

Simplifying enum definition?

I like to use some enums in Swift 3. They are also used as index to an array. So they are Int. I defined them as:
enum TypeOfArray: Int {
case src = 0, dst, srcCache, n
static var Start: Int { return 0 }
static var End : Int { return n.rawValue - 1 }
static let allValues = [src, srcCache, dst]
init() {
self = .n
}
}
So using .Start and .End I can use them as loop limits. But whenever I use the names "src" or "dst" itself, I have to add ".rawValue" to get the numeric value to be used as an index.
Is there any way to make it more convenience and to shorten it? (looks very complicated to me for such a simple task)

Class Array in D

Can I create a class array in D? Something like:
interface A {}
class AA: A {}
class AB: A {}
class AC: A {}
ClassList!A list = new ClassList!A {AA, AB, AC};
void testf(ulong testv) {
A a = new list[testv];
}
Yes, that's possible, though not necessarily exactly how you have it there. You can make a list of types with a type tuple:
import std.typetuple;
alias list = TypeTuple!(AA, AB, AC);
But, you can't index it like an array at runtime; trying new list[n] will be a compile time error. Instead, you'll create a helper function that loops over it and returns the instance, like this:
foreach(index, type; list)
if(index == testv) {
a = new type();
break;
}
The way that compiles is foreach(foo; compile_time_list) actually becomes a big unrolled loop. The generated code is as if you wrote:
if(0 == testv) { a = new AA(); goto end; }
if(1 == testv) { a = new AB(); goto end; }
if(2 == testv) { a = new AC(); goto end; }
end:
So that works with runtime values, bridging the gap between the compile time list and the array index you want.
It's worth noting that this isn't necessarily the most efficient, but unless your class list has like a thousand entries, i doubt that would matter. Another option for more speed is to generate a switch statement at compile time from your list, then mix that in. The switch statement will compile to a more efficient lookup table. But odds are the simple loop is fine.
Anyway, putting it together, we get:
import std.stdio;
interface A {}
class AA: A {}
class AB: A {}
class AC: A {}
import std.typetuple;
alias list = TypeTuple!(AA, AB, AC);
void testf(ulong testv) {
A a;
foreach(index, type; list)
if(index == testv) {
a = new type();
break;
}
if(a is null)
writeln("bad type index");
else {
writeln(typeid(cast(Object) a), " was created");
}
}
void main() {
testf(0);
testf(1);
testf(2);
testf(3);
}
Result:
$ ./test50
test50.AA was created
test50.AB was created
test50.AC was created
bad type index
just what we wanted.
BTW, the typeid(cast(Object) a) might look weird, that's fetching the dynamic class of the type. It has to cast to Object first because otherwise, it would print the interface name. Since D interfaces aren't necessarily D classes (they can also be COM objects or C++ classes), the typeid isn't always available. A cast to Object ensures that it is a D class and thus grabs the run time type details.
EDIT: I saw you asked on the newsgroup as well for checking the base class in the loop. Here's how to do that:
You could write your own tuple template for it, or just let the
compile fail on the factory function: the A a = new T(); will
fail if A isn't a base class or interface of T.
Putting the check in the list could look like this:
bool checkClassList(Base, T...)() {
foreach(t; T) {
static if(!is(t : Base))
static assert(0, t.stringof ~ " is not a child of " ~ Base.stringof);
}
return true;
}
template ClassList(Base, T...) if(checkClassList!(Base, T)) {
alias ClassList = T;
}
Usage:
alias list = ClassList!(A, AA, AB, AC); // good
add:
class B {}
alias list = ClassList!(A, AA, AB, AC, B);
and get error:
test50.d(12): Error: static assert "B is not a child of A"
test50.d(19): instantiated from here: checkClassList!(A, AA, AB, AC, B)

Convert objective-c typedef to its string equivalent

Assuming that I have a typedef declared in my .h file as such:
typedef enum {
JSON,
XML,
Atom,
RSS
} FormatType;
I would like to build a function that converts the numeric value of the typedef to a string. For example, if the message [self toString:JSON] was sent; it would return 'JSON'.
The function would look something like this:
-(NSString *) toString:(FormatType)formatType {
//need help here
return [];
}
Incidentally, if I try this syntax
[self toString:FormatType.JSON];
to pass the typedef value to the method, I get an error. What am I missing?
This is really a C question, not specific to Objective-C (which is a superset of the C language). Enums in C are represented as integers. So you need to write a function that returns a string given an enum value. There are many ways to do this. An array of strings such that the enum value can be used as an index into the array or a map structure (e.g. an NSDictionary) that maps an enum value to a string work, but I find that these approaches are not as clear as a function that makes the conversion explicit (and the array approach, although the classic C way is dangerous if your enum values are not continguous from 0). Something like this would work:
- (NSString*)formatTypeToString:(FormatType)formatType {
NSString *result = nil;
switch(formatType) {
case JSON:
result = #"JSON";
break;
case XML:
result = #"XML";
break;
case Atom:
result = #"Atom";
break;
case RSS:
result = #"RSS";
break;
default:
[NSException raise:NSGenericException format:#"Unexpected FormatType."];
}
return result;
}
Your related question about the correct syntax for an enum value is that you use just the value (e.g. JSON), not the FormatType.JSON sytax. FormatType is a type and the enum values (e.g. JSON, XML, etc.) are values that you can assign to that type.
You can't do it easily. In C and Objective-C, enums are really just glorified integer constants. You'll have to generate a table of names yourself (or with some preprocessor abuse). For example:
// In a header file
typedef enum FormatType {
JSON,
XML,
Atom,
RSS
} FormatType;
extern NSString * const FormatType_toString[];
// In a source file
// initialize arrays with explicit indices to make sure
// the string match the enums properly
NSString * const FormatType_toString[] = {
[JSON] = #"JSON",
[XML] = #"XML",
[Atom] = #"Atom",
[RSS] = #"RSS"
};
...
// To convert enum to string:
NSString *str = FormatType_toString[theEnumValue];
The danger of this approach is that if you ever change the enum, you have to remember to change the array of names. You can solve this problem with some preprocessor abuse, but it's tricky and ugly.
Also note that this assumes you have a valid enum constant. If you have an integer value from an untrusted source, you additionally need to do a check that your constant is valid, e.g. by including a "past max" value in your enum, or by checking if it's less than the array length, sizeof(FormatType_toString) / sizeof(FormatType_toString[0]).
My solution:
edit: I've added even a better solution at the end, using Modern Obj-C
1.Put names as keys in an array.
Make sure the indexes are the appropriate enums, and in the right order (otherwise exception).
note: names is a property synthesized as *_names*;
code was not checked for compilation, but I used the same technique in my app.
typedef enum {
JSON,
XML,
Atom,
RSS
} FormatType;
+ (NSArray *)names
{
static NSMutableArray * _names = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_names = [NSMutableArray arrayWithCapacity:4];
[_names insertObject:#"JSON" atIndex:JSON];
[_names insertObject:#"XML" atIndex:XML];
[_names insertObject:#"Atom" atIndex:Atom];
[_names insertObject:#"RSS" atIndex:RSS];
});
return _names;
}
+ (NSString *)nameForType:(FormatType)type
{
return [[self names] objectAtIndex:type];
}
//
2.Using Modern Obj-C you we can use a dictionary to tie descriptions to keys in the enum.Order DOES NOT matter.
typedef NS_ENUM(NSUInteger, UserType) {
UserTypeParent = 0,
UserTypeStudent = 1,
UserTypeTutor = 2,
UserTypeUnknown = NSUIntegerMax
};
#property (nonatomic) UserType type;
+ (NSDictionary *)typeDisplayNames
{
return #{#(UserTypeParent) : #"Parent",
#(UserTypeStudent) : #"Student",
#(UserTypeTutor) : #"Tutor",
#(UserTypeUnknown) : #"Unknown"};
}
- (NSString *)typeDisplayName
{
return [[self class] typeDisplayNames][#(self.type)];
}
Usage (in a class instance method):
NSLog(#"%#", [self typeDisplayName]);
Combining #AdamRosenfield answer, #Christoph comment and another trick to handle plain C enums I suggest:
// In a header file
typedef enum {
JSON = 0, // explicitly indicate starting index
XML,
Atom,
RSS,
FormatTypeCount, // keep track of the enum size automatically
} FormatType;
extern NSString *const FormatTypeName[FormatTypeCount];
// In a source file
NSString *const FormatTypeName[FormatTypeCount] = {
[JSON] = #"JSON",
[XML] = #"XML",
[Atom] = #"Atom",
[RSS] = #"RSS",
};
// Usage
NSLog(#"%#", FormatTypeName[XML]);
In the worst case - like if you change the enum but forget to change the names array - it will return nil for this key.
define typedef enum in class header:
typedef enum {
IngredientType_text = 0,
IngredientType_audio = 1,
IngredientType_video = 2,
IngredientType_image = 3
} IngredientType;
write a method like this in class:
+ (NSString*)typeStringForType:(IngredientType)_type {
NSString *key = [NSString stringWithFormat:#"IngredientType_%i", _type];
return NSLocalizedString(key, nil);
}
have the strings inside Localizable.strings file:
/* IngredientType_text */
"IngredientType_0" = "Text";
/* IngredientType_audio */
"IngredientType_1" = "Audio";
/* IngredientType_video */
"IngredientType_2" = "Video";
/* IngredientType_image */
"IngredientType_3" = "Image";
I would use the compiler's # string token (along with macros to make it all more compact):
#define ENUM_START \
NSString* ret; \
switch(value) {
#define ENUM_CASE(evalue) \
case evalue: \
ret = ##evalue; \
break;
#define ENUM_END \
} \
return ret;
NSString*
_CvtCBCentralManagerStateToString(CBCentralManagerState value)
{
ENUM_START
ENUM_CASE(CBCentralManagerStateUnknown)
ENUM_CASE(CBCentralManagerStateResetting)
ENUM_CASE(CBCentralManagerStateUnsupported)
ENUM_CASE(CBCentralManagerStateUnauthorized)
ENUM_CASE(CBCentralManagerStatePoweredOff)
ENUM_CASE(CBCentralManagerStatePoweredOn)
ENUM_END
}
I like the #define way of doing this:
// Place this in your .h file, outside the #interface block
typedef enum {
JPG,
PNG,
GIF,
PVR
} kImageType;
#define kImageTypeArray #"JPEG", #"PNG", #"GIF", #"PowerVR", nil
// Place this in the .m file, inside the #implementation block
// A method to convert an enum to string
-(NSString*) imageTypeEnumToString:(kImageType)enumVal
{
NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray];
return [imageTypeArray objectAtIndex:enumVal];
}
source (source no longer available)
I made a sort of mix of all solutions found on this page to create mine, it's a kind of object oriented enum extension or
something.
In fact if you need more than just constants (i.e. integers), you probably need a model object (We're all talking about MVC, right?)
Just ask yourself the question before using this, am I right, don't you, in fact, need a real model object, initialized from a webservice, a plist, an SQLite database or CoreData ?
Anyway here comes the code (MPI is for "My Project Initials", everybody use this or their name, it seems) :
MyWonderfulType.h :
typedef NS_ENUM(NSUInteger, MPIMyWonderfulType) {
MPIMyWonderfulTypeOne = 1,
MPIMyWonderfulTypeTwo = 2,
MPIMyWonderfulTypeGreen = 3,
MPIMyWonderfulTypeYellow = 4,
MPIMyWonderfulTypePumpkin = 5
};
#import <Foundation/Foundation.h>
#interface MyWonderfulType : NSObject
+ (NSString *)displayNameForWonderfulType:(MPIMyWonderfulType)wonderfulType;
+ (NSString *)urlForWonderfulType:(MPIMyWonderfulType)wonderfulType;
#end
And MyWonderfulType.m :
#import "MyWonderfulType.h"
#implementation MyWonderfulType
+ (NSDictionary *)myWonderfulTypeTitles
{
return #{
#(MPIMyWonderfulTypeOne) : #"One",
#(MPIMyWonderfulTypeTwo) : #"Two",
#(MPIMyWonderfulTypeGreen) : #"Green",
#(MPIMyWonderfulTypeYellow) : #"Yellow",
#(MPIMyWonderfulTypePumpkin) : #"Pumpkin"
};
}
+ (NSDictionary *)myWonderfulTypeURLs
{
return #{
#(MPIMyWonderfulTypeOne) : #"http://www.theone.com",
#(MPIMyWonderfulTypeTwo) : #"http://www.thetwo.com",
#(MPIMyWonderfulTypeGreen) : #"http://www.thegreen.com",
#(MPIMyWonderfulTypeYellow) : #"http://www.theyellow.com",
#(MPIMyWonderfulTypePumpkin) : #"http://www.thepumpkin.com"
};
}
+ (NSString *)displayNameForWonderfulType:(MPIMyWonderfulType)wonderfulType {
return [MPIMyWonderfulType myWonderfulTypeTitles][#(wonderfulType)];
}
+ (NSString *)urlForWonderfulType:(MPIMyWonderfulType)wonderfulType {
return [MPIMyWonderfulType myWonderfulTypeURLs][#(wonderfulType)];
}
#end
Another solution:
typedef enum BollettinoMavRavTypes {
AMZCartServiceOperationCreate,
AMZCartServiceOperationAdd,
AMZCartServiceOperationGet,
AMZCartServiceOperationModify
} AMZCartServiceOperation;
#define AMZCartServiceOperationValue(operation) [[[NSArray alloc] initWithObjects: #"CartCreate", #"CartAdd", #"CartGet", #"CartModify", nil] objectAtIndex: operation];
In your method you can use:
NSString *operationCheck = AMZCartServiceOperationValue(operation);
Improved #yar1vn answer by dropping string dependency:
#define VariableName(arg) (#""#arg)
typedef NS_ENUM(NSUInteger, UserType) {
UserTypeParent = 0,
UserTypeStudent = 1,
UserTypeTutor = 2,
UserTypeUnknown = NSUIntegerMax
};
#property (nonatomic) UserType type;
+ (NSDictionary *)typeDisplayNames
{
return #{#(UserTypeParent) : VariableName(UserTypeParent),
#(UserTypeStudent) : VariableName(UserTypeStudent),
#(UserTypeTutor) : VariableName(UserTypeTutor),
#(UserTypeUnknown) : VariableName(UserTypeUnknown)};
}
- (NSString *)typeDisplayName
{
return [[self class] typeDisplayNames][#(self.type)];
}
Thus when you'll change enum entry name corresponding string will be changed.
Useful in case if you are not going to show this string to user.
#pixel added the most brilliant answer here:
https://stackoverflow.com/a/24255387/1364257
Please, upvote him!
He uses the neat X macro from the 1960's. (I've changed his code a bit for the modern ObjC)
#define X(a, b, c) a b,
enum ZZObjectType {
XXOBJECTTYPE_TABLE
};
typedef NSUInteger TPObjectType;
#undef X
#define XXOBJECTTYPE_TABLE \
X(ZZObjectTypeZero, = 0, #"ZZObjectTypeZero") \
X(ZZObjectTypeOne, , #"ZZObjectTypeOne") \
X(ZZObjectTypeTwo, , #"ZZObjectTypeTwo") \
X(ZZObjectTypeThree, , #"ZZObjectTypeThree")
+ (NSString*)nameForObjectType:(ZZObjectType)objectType {
#define X(a, b, c) #(a):c,
NSDictionary *dict = #{XXOBJECTTYPE_TABLE};
#undef X
return dict[objectType];
}
That's it. Clean and neat.
Thanks to #pixel! https://stackoverflow.com/users/21804/pixel
Given an enum definition like:
typedef NS_ENUM(NSInteger, AssetIdentifier) {
Isabella,
William,
Olivia
};
We can define a macro to convert an enum value to its corresponding string, as shown below.
#define AssetIdentifier(asset) \
^(AssetIdentifier identifier) { \
switch (identifier) { \
case asset: \
default: \
return ##asset; \
} \
}(asset)
The switch statement used in the block is for type checking, and also to get the auto-complete support in Xcode.
I had a large enumerated type I wanted to convert it into an NSDictionary lookup. I ended up using sed from OSX terminal as:
$ sed -E 's/^[[:space:]]{1,}([[:alnum:]]{1,}).*$/ #(\1) : #"\1",/g' ObservationType.h
which can be read as: 'capture the first word on the line and output #(word) : #"word",'
This regex converts the enum in a header file named 'ObservationType.h' which contains:
typedef enum : int {
ObservationTypePulse = 1,
ObservationTypeRespRate = 2,
ObservationTypeTemperature = 3,
.
.
}
into something like:
#(ObservationTypePulse) : #"ObservationTypePulse",
#(ObservationTypeRespRate) : #"ObservationTypeRespRate",
#(ObservationTypeTemperature) : #"ObservationTypeTemperature",
.
.
which can then be wrapped in a method using modern objective-c syntax #{ } (as explained by #yar1vn above) to create a NSDictionary lookup :
-(NSDictionary *)observationDictionary
{
static NSDictionary *observationDictionary;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
observationDictionary = [[NSDictionary alloc] initWithDictionary:#{
#(ObservationTypePulse) : #"ObservationTypePulse",
#(ObservationTypeRespRate) : #"ObservationTypeRespRate",
.
.
}];
});
return observationDictionary;
}
The dispatch_once boiler-plate is just to ensure that the static variable is initialised in a thread-safe manner.
Note: I found the sed regex expression on OSX odd - when I tried to use + to match 'one or more' it didn't work and had to resort to using {1,} as a replacement
I use a variation on Barry Walk's answer, that in order of importance:
Allows the compiler to check for missing case clauses (it can't if you have a default clause).
Uses an Objective-C typical name (rather than a Java like name).
Raises a specific exception.
Is shorter.
EG:
- (NSString*)describeFormatType:(FormatType)formatType {
switch(formatType) {
case JSON:
return #"JSON";
case XML:
return #"XML";
case Atom:
return #"Atom";
case RSS:
return #"RSS";
}
[NSException raise:NSInvalidArgumentException format:#"The given format type number, %ld, is not known.", formatType];
return nil; // Keep the compiler happy - does not understand above line never returns!
}
I combined several approaches here.
I like the idea of the preprocessor and the indexed list.
There's no extra dynamic allocation, and because of the inlining the compiler might be able to optimize the lookup.
typedef NS_ENUM(NSUInteger, FormatType) { FormatTypeJSON = 0, FormatTypeXML, FormatTypeAtom, FormatTypeRSS, FormatTypeCount };
NS_INLINE NSString *FormatTypeToString(FormatType t) {
if (t >= FormatTypeCount)
return nil;
#define FormatTypeMapping(value) [value] = ##value
NSString *table[FormatTypeCount] = {FormatTypeMapping(FormatTypeJSON),
FormatTypeMapping(FormatTypeXML),
FormatTypeMapping(FormatTypeAtom),
FormatTypeMapping(FormatTypeRSS)};
#undef FormatTypeMapping
return table[t];
}
First of all, with regards to FormatType.JSON: JSON is not a member of FormatType, it's a possible value of the type. FormatType isn't even a composite type — it's a scalar.
Second, the only way to do this is to create a mapping table. The more common way to do this in Objective-C is to create a series of constants referring to your "symbols", so you'd have NSString *FormatTypeJSON = #"JSON" and so on.
the following provides a solution such that to add a new enum requires
only a one-line edit, similar work to adding a single line in an enum {} list.
//------------------------------------------------------------------------------
// enum to string example
#define FOR_EACH_GENDER(tbd) \
tbd(GENDER_MALE) \
tbd(GENDER_FEMALE) \
tbd(GENDER_INTERSEX) \
#define ONE_GENDER_ENUM(name) name,
enum
{
FOR_EACH_GENDER(ONE_GENDER_ENUM)
MAX_GENDER
};
#define ONE_GENDER(name) #name,
static const char *enumGENDER_TO_STRING[] =
{
FOR_EACH_GENDER(ONE_GENDER)
};
// access string name with enumGENDER_TO_STRING[value]
// or, to be safe converting from a untrustworthy caller
static const char *enumGenderToString(unsigned int value)
{
if (value < MAX_GENDER)
{
return enumGENDER_TO_STRING[value];
}
return NULL;
}
static void printAllGenders(void)
{
for (int ii = 0; ii < MAX_GENDER; ii++)
{
printf("%d) gender %s\n", ii, enumGENDER_TO_STRING[ii]);
}
}
//------------------------------------------------------------------------------
// you can assign an arbitrary value and/or information to each enum,
#define FOR_EACH_PERSON(tbd) \
tbd(2, PERSON_FRED, "Fred", "Weasley", GENDER_MALE, 12) \
tbd(4, PERSON_GEORGE, "George", "Weasley", GENDER_MALE, 12) \
tbd(6, PERSON_HARRY, "Harry", "Potter", GENDER_MALE, 10) \
tbd(8, PERSON_HERMIONE, "Hermione", "Granger", GENDER_FEMALE, 10) \
#define ONE_PERSON_ENUM(value, ename, first, last, gender, age) ename = value,
enum
{
FOR_EACH_PERSON(ONE_PERSON_ENUM)
};
typedef struct PersonInfoRec
{
int value;
const char *ename;
const char *first;
const char *last;
int gender;
int age;
} PersonInfo;
#define ONE_PERSON_INFO(value, ename, first, last, gender, age) \
{ ename, #ename, first, last, gender, age },
static const PersonInfo personInfo[] =
{
FOR_EACH_PERSON(ONE_PERSON_INFO)
{ 0, NULL, NULL, NULL, 0, 0 }
};
// note: if the enum values are not sequential, you need another way to lookup
// the information besides personInfo[ENUM_NAME]
static void printAllPersons(void)
{
for (int ii = 0; ; ii++)
{
const PersonInfo *pPI = &personInfo[ii];
if (!pPI->ename)
{
break;
}
printf("%d) enum %-15s %8s %-8s %13s %2d\n",
pPI->value, pPI->ename, pPI->first, pPI->last,
enumGenderToString(pPI->gender), pPI->age);
}
}
Every answer here basically says the same thing, create a regular enum and then use a custom getter to switch between strings.
I employ a much simpler solution that is faster, shorter, and cleaner—using Macros!
#define kNames_allNames ((NSArray <NSString *> *)#[#"Alice", #"Bob", #"Eve"])
#define kNames_alice ((NSString *)kNames_allNames[0])
#define kNames_bob ((NSString *)kNames_allNames[1])
#define kNames_eve ((NSString *)kNames_allNames[2])
Then you can simply start to type kNam... and autocomplete will display the lists you desire!
Additionally, if you want to handle logic for all the names at once you can simply fast enumerate the literal array in order, as follows:
for (NSString *kName in kNames_allNames) {}
Lastly, the NSString casting in the macros ensures behavior similar to typedef!
Enjoy!
I think I just created the most elegant approach to solve this.
(inspired by #AdamRosenfield)
For example, you may declare the enum like this:
typedef NS_ENUM(int, MyEnum) {
MyEnumDefault,
MyEnumOff,
MyEnumOn,
MyEnumMixed,
};
static NSString * const __SGEnumMap_MyEnum[] = {
[MyEnumDefault] = #"default",
[MyEnumOff] = #"off",
[MyEnumOn] = #"on",
[MyEnumMixed] = #"mixed"
};
SGENUM_DEFINE_ToString(MyEnum)
SGENUM_DEFINE_FromString(MyEnum)
Then, you get the two functions to convert between enums and strings:
NSString *result = MyEnumToString(MyEnumOn);
NSLog(#"%#", result);
// on
MyEnum value = MyEnumFromString(#"off", -1); // -1 as the default value
NSLog(#"%d", result);
// 1
Is that cool? Here are the magic macros:
#define SGENUM_DEFINE_ToString(name) NS_INLINE NSString *name##ToString(name value){\
int count = sizeof(__SGEnumMap_##name) / sizeof(NSString *);\
if (value > count || value < 0) return nil;\
return __SGEnumMap_##name[value];\
}
#define SGENUM_DEFINE_FromString(name) NS_INLINE name name##FromString(NSString *input, int defaultValue) {\
int count = sizeof(__SGEnumMap_##name) / sizeof(NSString *);\
for (int i = 0; i < count; i++) {\
NSString *str = __SGEnumMap_##name[i];\
if ([str isEqual:input]) {\
return i;\
}\
}\
return defaultValue;\
}
Many answers all fairly good.
If you are after a generic, Objective C solution that uses some macros...
Key feature is it uses the enum as an index into a static array of NSString constants.
the array itself is wrapped into a function to make it more like the suite of NSStringFromXXX functions prevalent in the Apple APIs.
you will need to #import "NSStringFromEnum.h" found here
http://pastebin.com/u83RR3Vk
[EDIT]
also needs #import "SW+Variadic.h" found here http://pastebin.com/UEqTzYLf
Example 1 : completely define a NEW enum typedef, with string converters.
in myfile.h
#import "NSStringFromEnum.h"
#define define_Dispatch_chain_cmd(enum)\
enum(chain_done,=0)\
enum(chain_entry)\
enum(chain_bg)\
enum(chain_mt)\
enum(chain_alt)\
enum(chain_for_c)\
enum(chain_while)\
enum(chain_continue_for)\
enum(chain_continue_while)\
enum(chain_break_for)\
enum(chain_break_while)\
enum(chain_previous)\
enum(chain_if)\
enum(chain_else)\
interface_NSString_Enum_DefinitionAndConverters(Dispatch_chain_cmd)
in myfile.m:
#import "myfile.h"
implementation_NSString_Enum_Converters(Dispatch_chain_cmd)
to use :
NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value);
NSStringFromEnumDispatch_chain_cmd(chain_for_c) returns #"chain_for_c"
enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);
enumDispatch_chain_cmdFromNSString(#"chain_previous") returns chain_previous
Example 2: provide conversion routines for an existing enum
also demonstrates using a settings string, and renaming the typename used in the functions.
in myfile.h
#import "NSStringFromEnum.h"
#define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask
interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)
in myfile.m:
// we can put this in the .m file as we are not defining a typedef, just the strings.
#define define_CAEdgeAntialiasingMask(enum)\
enum(kCALayerLeftEdge)\
enum(kCALayerRightEdge)\
enum(kCALayerBottomEdge)\
enum(kCALayerTopEdge)
implementation_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)
Here is working -> https://github.com/ndpiparava/ObjcEnumString
//1st Approach
#define enumString(arg) (#""#arg)
//2nd Approach
+(NSString *)secondApproach_convertEnumToString:(StudentProgressReport)status {
char *str = calloc(sizeof(kgood)+1, sizeof(char));
int goodsASInteger = NSSwapInt((unsigned int)kgood);
memcpy(str, (const void*)&goodsASInteger, sizeof(goodsASInteger));
NSLog(#"%s", str);
NSString *enumString = [NSString stringWithUTF8String:str];
free(str);
return enumString;
}
//Third Approcah to enum to string
NSString *const kNitin = #"Nitin";
NSString *const kSara = #"Sara";
typedef NS_ENUM(NSUInteger, Name) {
NameNitin,
NameSara,
};
+ (NSString *)thirdApproach_convertEnumToString :(Name)weekday {
__strong NSString **pointer = (NSString **)&kNitin;
pointer +=weekday;
return *pointer;
}
Depending on your needs, you could alternatively use compiler directives to simulate the behaviour you are looking for.
#define JSON #"JSON"
#define XML #"XML"
#define Atom #"Atom"
#define RSS #"RSS"
Just remember the usual compiler shortcomings, (not type safe, direct copy-paste makes source file larger)

Resources