Implementation of boost::any container substitute 35/17935/17
authorRichard Underhill <r.underhill@partner.samsung.com>
Fri, 21 Mar 2014 14:05:18 +0000 (14:05 +0000)
committerRichard Underhill <r.underhill@partner.samsung.com>
Fri, 21 Mar 2014 14:05:18 +0000 (14:05 +0000)
[Issue#] N/A

[Problem]

[Cause]

[Solution]

Change-Id: I8a6ec38bc0c3932f90aff6b5debb972f2fb9fa9a
Signed-off-by: Richard Underhill <r.underhill@partner.samsung.com>
automated-tests/dali-test-suite/common/.gitignore
automated-tests/dali-test-suite/common/file.list
automated-tests/dali-test-suite/common/tslist
automated-tests/dali-test-suite/common/utc-Dali-Any.cpp [new file with mode: 0644]
capi/dali/public-api/dali-core-capi-internal.h
capi/dali/public-api/file.list
capi/dali/public-api/object/any.h [new file with mode: 0644]
capi/dali_doc.h
dali/public-api/file.list
dali/public-api/object/any.cpp [new file with mode: 0644]

index ff93888..b5ebacf 100644 (file)
@@ -1,3 +1,4 @@
 TARGETS += \
         utc-Dali-LocklessBuffer \
         utc-Dali-Vector \
+        utc-Dali-Any \
index c3dd165..81d0d6d 100644 (file)
@@ -1,2 +1,3 @@
 /dali-test-suite/common/utc-Dali-LocklessBuffer
 /dali-test-suite/common/utc-Dali-Vector
+/dali-test-suite/common/utc-Dali-Any
diff --git a/automated-tests/dali-test-suite/common/utc-Dali-Any.cpp b/automated-tests/dali-test-suite/common/utc-Dali-Any.cpp
new file mode 100644 (file)
index 0000000..b5d7442
--- /dev/null
@@ -0,0 +1,264 @@
+///
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Flora License, Version 1.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://floralicense.org/license/
+//
+// 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.
+//
+
+#include <iostream>
+
+#include <stdlib.h>
+#include <tet_api.h>
+
+#include <dali/public-api/dali-core.h>
+
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+
+static void Startup();
+static void Cleanup();
+
+extern "C" {
+  void (*tet_startup)() = Startup;
+  void (*tet_cleanup)() = Cleanup;
+}
+
+enum {
+  POSITIVE_TC_IDX = 0x01,
+  NEGATIVE_TC_IDX,
+};
+
+#define MAX_NUMBER_OF_TESTS 10000
+extern "C" {
+  struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS];
+}
+
+TEST_FUNCTION( UtcDaliAnyConstructors, POSITIVE_TC_IDX );
+TEST_FUNCTION( UtcDaliAnyAssignmentOperators, POSITIVE_TC_IDX );
+TEST_FUNCTION( UtcDaliAnyNegativeAssignmentOperators, NEGATIVE_TC_IDX );
+TEST_FUNCTION( UtcDaliAnyGetType, POSITIVE_TC_IDX );
+TEST_FUNCTION( UtcDaliAnyGet, POSITIVE_TC_IDX );
+TEST_FUNCTION( UtcDaliAnyNegativeGet, NEGATIVE_TC_IDX );
+
+// Called only once before first test is run.
+static void Startup()
+{
+}
+
+// Called only once after last test is run
+static void Cleanup()
+{
+}
+
+static void UtcDaliAnyConstructors()
+{
+  TestApplication application;
+
+  tet_infoline("Test Any constructors.");
+
+  // Test default constructor.
+  Any value;
+
+  DALI_TEST_CHECK( typeid( void ) == value.GetType() );
+
+  // Test constructor Any( const Type& )
+  Any value1 = 4u;
+
+  // Test constructor Any( const Any& )
+  Any value2 = value1;
+
+  // Test constructor Any( const Any& ) with a non initialized Any
+  Any value3 = value;
+
+  DALI_TEST_CHECK( typeid( unsigned int ) == value1.GetType() );
+  DALI_TEST_CHECK( typeid( unsigned int ) == value2.GetType() );
+  DALI_TEST_CHECK( typeid( void ) == value3.GetType() );
+
+  unsigned int uiValue1 = 0u;
+  unsigned int uiValue2 = 0u;
+  value1.Get( uiValue1 );
+  value2.Get( uiValue2 );
+
+  DALI_TEST_EQUALS( uiValue1, uiValue2, TEST_LOCATION );
+}
+
+static void UtcDaliAnyAssignmentOperators()
+{
+  TestApplication application;
+
+  tet_infoline("Test assignment operators.");
+
+  float fValue = 0.f;
+
+  Any value1;
+
+  value1 = 4.f; // Test operator=( const Type& ) when current object is not initialized.
+
+  value1.Get( fValue );
+
+  DALI_TEST_EQUALS( fValue, 4.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
+  Any value2 = 0.f;
+
+  value2 = 9.f; // Test operator=( const Type& ).
+
+  value2.Get( fValue );
+
+  DALI_TEST_EQUALS( fValue, 9.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
+  Any value3 = 5.f;
+
+  value1 = value3; // Test operator=( const Any& ).
+
+  value1.Get( fValue );
+
+  DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
+  Any value4;
+
+  value4 = value3; // Test operator=( const Any& ) when current object is not initialized.
+
+  value4.Get( fValue );
+
+  DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
+  // Test assign a value to value3 doesn't modify value1.
+  value3 = 3.f;
+
+  value1.Get( fValue );
+
+  DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
+  value3.Get( fValue );
+
+  DALI_TEST_EQUALS( fValue, 3.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
+  // Test the branch when copying the same object.
+  Any value5 = 3.f;
+  Any& value6( value5 );
+
+  value6 = value5;
+
+  value6.Get( fValue );
+  DALI_TEST_EQUALS( fValue, 3.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+}
+
+static void UtcDaliAnyNegativeAssignmentOperators()
+{
+  TestApplication application;
+
+  tet_infoline("Test assignment operators.");
+
+  Any value1 = 4.f;
+  Any value2 = 5u;
+
+  bool assert = false;
+
+  try
+  {
+    value1 = value2; // Test operator=( const Any& );
+  }
+  catch( Dali::DaliException& e )
+  {
+    tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() );
+    assert = true;
+  }
+
+  if( assert )
+  {
+    tet_result( TET_PASS );
+  }
+  else
+  {
+    tet_result( TET_FAIL );
+  }
+}
+
+static void UtcDaliAnyGetType()
+{
+  TestApplication application;
+
+  tet_infoline("Test GetType().");
+
+  Any value;
+
+  DALI_TEST_CHECK( typeid( void ) == value.GetType() );
+
+  value = 5.f;
+
+  DALI_TEST_CHECK( typeid( float ) == value.GetType() );
+}
+
+static void UtcDaliAnyGet()
+{
+  TestApplication application;
+
+  tet_infoline("Test Get().");
+
+  Any value1( 5.f );
+
+  float fValue = value1.Get<float>();
+
+  DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+
+  fValue = 0.f;
+  value1.Get( fValue );
+  DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION );
+}
+
+static void UtcDaliAnyNegativeGet()
+{
+  TestApplication application;
+  tet_infoline("Test Get().");
+
+  Any value1;
+  Any value2( 5.f );
+
+  bool assert1 = false;
+  bool assert2 = false;
+
+  unsigned int uiValue = 0u;
+
+  try
+  {
+    uiValue = value1.Get< unsigned int >();
+  }
+
+  catch( Dali::DaliException& e )
+  {
+    tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() );
+    assert1 = true;
+  }
+
+  try
+  {
+    uiValue = value2.Get< unsigned int >();
+  }
+
+  catch( Dali::DaliException& e )
+  {
+    tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() );
+    assert2 = true;
+  }
+
+  if( assert1 && assert2 )
+  {
+    tet_result( TET_PASS );
+  }
+  else
+  {
+    tet_result( TET_FAIL );
+  }
+  uiValue++;  // supresss warning from unused variable
+}
+
index 3a99f22..1fba8fe 100644 (file)
@@ -88,6 +88,7 @@
 
 #include <dali/public-api/modeling/material.h>
 
+#include <dali/public-api/object/any.h>
 #include <dali/public-api/object/base-handle.h>
 #include <dali/public-api/object/base-object.h>
 #include <dali/public-api/object/constrainable.h>
index 2df2ff0..eb96160 100644 (file)
@@ -84,6 +84,7 @@ capi_devel_modeling_header_files =  \
   $(capi_devel_src_dir)/modeling/material.h
 
 capi_devel_object_header_files =  \
+  $(capi_devel_src_dir)/object/any.h \
   $(capi_devel_src_dir)/object/base-handle.h \
   $(capi_devel_src_dir)/object/base-object.h \
   $(capi_devel_src_dir)/object/constrainable.h \
diff --git a/capi/dali/public-api/object/any.h b/capi/dali/public-api/object/any.h
new file mode 100644 (file)
index 0000000..2f49a21
--- /dev/null
@@ -0,0 +1,403 @@
+#ifndef __DALI_ANY_TYPE_H__
+#define __DALI_ANY_TYPE_H__
+
+//
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Flora License, Version 1.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://floralicense.org/license/
+//
+// 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.
+//
+
+/**
+ * @addtogroup CAPI_DALI_OBJECT_MODULE
+ * @{
+ */
+
+// EXTERNAL INCLUDES
+#include <typeinfo>   // operator typeid
+
+
+// INTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+
+namespace Dali DALI_IMPORT_API
+{
+
+/**
+ * Stores a value of any type.
+ *
+ * @note Assignments of values with different types are now allowed to replicate Any behaviour
+ *
+ * Examples of use:
+ * \code{.cpp}
+ * Any uintVariable = 5u;
+ * Any floatVariable( 4.5f );
+ * Any strVariable( std::string( "Hello world" ) );
+ * uintVariable = 1u;
+ * unsigned int variable = AnyCast< unsigned int >( uintVariable );
+ * if ( typeid( int ) == uintVariable.GetType() )
+ * \endcode
+ */
+class Any
+{
+public:
+  /**
+   * Default constructor.
+   */
+  Any();
+
+  /**
+   * Destructor. Free resources.
+   */
+  ~Any();
+
+/**
+   * @brief Pass Assert message
+   *
+   * @param assertMessage Assert message to report
+   */
+  static void AssertAlways( const char* assertMessage );
+
+  /**
+   * @brief Constructs a Any type with the given value.
+   *
+   * @param[in] value The given value.
+   */
+  template<typename Type>
+  Any( const Type& value )
+  : mContainer( new AnyContainerImpl<Type>( value ) )
+  {
+  }
+
+  /**
+   * Copy Constructor.
+   * @param [in] any Any to be copied.
+   */
+  Any( const Any& any )
+  {
+    // If container isn't empty then copy the container?
+    if ( NULL != any.mContainer )
+    {
+      mContainer = any.mContainer->mCloneFunc( *any.mContainer );
+    }
+    else
+    {
+      // Otherwise mark new container as empty
+      mContainer = NULL;
+    }
+  }
+
+  /**
+   * @brief Assigns a given value to the Any type.
+   *
+   * @note If the types are different, then the current container will be re-created.
+   *
+   * @param[in] value The given value.
+   */
+  template<typename Type>
+  Any& operator=( const Type& value )
+  {
+
+    // If the container is empty then assign the new value
+    if ( NULL == mContainer )
+    {
+      mContainer = new AnyContainerImpl< Type >( value );
+    }
+    else
+    {
+      // Check to see if this type is compatible with current container?
+      if ( mContainer->GetType() == typeid( Type ) )
+      {
+        // Same type so just set the new value
+        static_cast< AnyContainerImpl< Type >* >( mContainer )->SetValue( value );
+      }
+      else
+      {
+        // Incompatible types, so delete old container and assign a new one with this type and value
+        mContainer->mDeleteFunc( mContainer );
+        mContainer = new AnyContainerImpl< Type >( value );
+      }
+    }
+    return *this;
+  }
+
+  /**
+   * @brief Assignment operator.
+   *
+   * @note Asserts if values of different types are assigned.
+   *
+   * @param [in] any Any to be assigned.
+   */
+  Any& operator=( const Any& any );
+
+  /**
+   * @brief Get a value of type Type from container
+   *
+   * @param type destination of type Type to write to
+   */
+  template<typename Type>
+  void Get( Type& type )
+  {
+    type = Get<Type>();
+  }
+
+  /**
+   * @brief Returns the type info of the stored value.
+   *
+   * @note Returns the info of the void type if there is no value stored.
+   */
+  const std::type_info& GetType() const;
+
+  /**
+   * @brief Retrieves the stored value in the Any type.
+   *
+   * @return The stored value.
+   */
+  template<typename Type>
+  const Type& Get() const
+  {
+
+    if ( NULL == mContainer )
+    {
+      AssertAlways( "Any::Get(). mContainer is NULL" );
+    }
+
+    // Check if the value has the same value than the Any type.
+    if( mContainer->GetType() != typeid( Type ) )
+    {
+      AssertAlways( "Any::Get(). Trying to retrieve a value of a different type than the template one." );
+    }
+    return static_cast< AnyContainerImpl< Type >* >( mContainer )->GetValue();
+  }
+
+  /**
+   * @brief Return pointer of Type to the value stored
+   *
+   * @return pointer to the value or NULL if no value is contained
+   */
+  template<typename Type>
+  Type* GetPointer()
+  {
+    if( NULL == mContainer )
+    {
+      return NULL;
+    }
+     // Check if the value has the same value than the Any type.
+    if( mContainer->GetType() != typeid( Type ) )
+    {
+      AssertAlways( "Any::GetPointer(). Trying to retrieve a pointer to a value of a different type than the template one." );
+    }
+    return static_cast< AnyContainerImpl< Type >* >( mContainer )->GetPointerToValue();
+  }
+
+  /**
+   * @brief Returns whether container holds a value
+   *
+   * @return true or false
+   */
+  bool Empty()
+  {
+    return ( NULL == mContainer ) ? true : false;
+  }
+
+  struct AnyContainerBase;    // Forward declaration for typedef
+  typedef AnyContainerBase* (*CloneFunc)( const AnyContainerBase& base );
+  typedef void (*DeleteFunc)( const AnyContainerBase* base );
+
+  /**
+   * Base container to hold type for match verification and instance cloning function
+   *
+   */
+  struct AnyContainerBase
+  {
+    /**
+     * @brief Constructor of base container
+     *
+     * @param type typeid of container
+     * @param cloneFunc Cloning function to replicate this container type
+     * @param deleteFunc Deleting function to destroy this container type
+     */
+    AnyContainerBase( const std::type_info& type, CloneFunc cloneFunc, DeleteFunc deleteFunc )
+    : mType( type ),
+      mCloneFunc( cloneFunc ),
+      mDeleteFunc( deleteFunc )
+    {}
+
+    /**
+     * @brief Get the typeid of this container
+     *
+     * @return type
+     */
+    const std::type_info& GetType() const
+    {
+      return mType;
+    }
+
+    const::std::type_info& mType;       // typeID
+    CloneFunc mCloneFunc;               // cloning function for this container
+    DeleteFunc mDeleteFunc;             // deleting function for this container
+  };
+
+
+  /**
+   * @brief Templated Clone function from container base
+   *
+   * @param base reference to container
+   */
+  template<typename Type>
+  struct AnyContainerImplCloner
+  {
+    static AnyContainerBase* Clone( const AnyContainerBase& base )
+    {
+      return new AnyContainerImpl< Type >( static_cast< AnyContainerImpl< Type > >( base ) );
+    }
+  };
+
+  /**
+   * @brief Templated Delete function from container base
+   *
+   * @param base pointer to container
+   */
+  template<typename Type>
+  struct AnyContainerImplDelete
+  {
+    static void Delete( const AnyContainerBase* base )
+    {
+      delete ( static_cast< const AnyContainerImpl< Type >* > ( base ) );
+    }
+  };
+
+  /**
+   * @brief Templated class to hold value for type
+   *
+   */
+  template<typename Type>
+  class AnyContainerImpl : public AnyContainerBase
+  {
+  public:
+
+    /**
+     * @brief Constructor to create container holding value of type Type
+     *
+     * @param value Value of Type
+     */
+    AnyContainerImpl( const Type& value )
+    : AnyContainerBase( typeid( Type ),
+                        static_cast< CloneFunc >( &AnyContainerImplCloner< Type >::Clone ),
+                        static_cast< DeleteFunc >( &AnyContainerImplDelete< Type >::Delete ) ),
+                        mValue( value )
+    {}
+
+    /**
+     * @brief Constructor to create new container of type from and existing container (cloning)
+     *
+     * @param base reference to base container to copy from
+     */
+    AnyContainerImpl( const AnyContainerBase& base )
+    : AnyContainerBase( typeid( Type ),
+                        static_cast< CloneFunc >( &AnyContainerImplCloner< Type >::Clone ),
+                        static_cast< DeleteFunc >( &AnyContainerImplDelete< Type >::Delete ) )
+    {
+      mValue = static_cast< const AnyContainerImpl& >( base ).GetValue();
+    }
+
+    /**
+     * @brief Get the container's stored value
+     *
+     * @return value of Type
+     */
+    const Type& GetValue() const
+    {
+      return mValue;
+    }
+
+    void SetValue( const Type& value )
+    {
+      mValue = value;
+    }
+
+    /**
+     * @brief Get a pointer to the value held
+     *
+     * @return pointer to the value of type Type
+     */
+    Type* GetPointerToValue()
+    {
+      return static_cast< Type* >( &mValue );
+    }
+
+    private:
+      Type mValue;
+  };
+
+  AnyContainerBase* mContainer;
+
+};
+
+/**
+ * AnyCast helper functions ( replicates boost functionality, but without exception generation )
+ */
+
+/**
+ * @brief Extract a pointer to the held type of an Any object from a pointer to that Any object (NULL if empty )
+ *
+ * @param any Pointer to an Any object
+ *
+ * @return Pointer to the Type held
+ */
+template<typename Type>inline Type* AnyCast( Any* any )
+{
+  return any->GetPointer<Type>();
+}
+
+/**
+ * @brief Extract a const pointer to the held type of an Any object from a pointer to that Any object (NULL if empty )
+ *
+ * @param any const Pointer to an Any object
+ *
+ * @return const Pointer to the Type held
+ */
+template<typename Type>inline const Type* AnyCast( const Any* any )
+{
+  return any->GetPointer<Type>();
+}
+
+/**
+ * @brief Extract a held value of type Type from an Any object from a reference to that Any object
+ *
+ * @param any reference to an Any object
+ *
+ * @return Type value of type Type
+ */
+template<typename Type>inline Type AnyCast( Any& any )
+{
+  return any.Get<Type>();
+}
+
+/**
+ * @brief Extract a held value of type Type from an Any object from a const reference to that Any object
+ *
+ * @param any reference to an Any object
+ *
+ * @return Type value of type Type
+ */
+template<typename Type>inline Type AnyCast( const Any& any )
+{
+  return any.Get<Type>();
+}
+
+} // namespace Dali
+
+/**
+ * @}
+ */
+#endif // __DALI_ANY_TYPE_H__
index 64eae78..ea74293 100644 (file)
@@ -253,6 +253,7 @@ Dali::ShortestDistanceInDomain()
  * <tr><td>@ref Dali::Property::Value </td><td>A value-type representing a property value.</td></tr>a
  * <tr><td>@ref Dali::RefObject </td><td>Base class for reference counted objects.</td></tr>
  * <tr><td>@ref Dali::IntrusivePtr </td><td>Template class used to point at Dali::RefObjects</td></tr>
+ * <tr><td>@ref Dali::Any </td><td>Class for objects that contain values of different types</td></tr>
  * </table>
  */
 
index ff48347..1e8d8c9 100644 (file)
@@ -79,6 +79,7 @@ public_api_src_files = \
   $(public_api_src_dir)/modeling/material.cpp \
   $(public_api_src_dir)/modeling/model.cpp \
   $(public_api_src_dir)/modeling/model-data.cpp \
+  $(public_api_src_dir)/object/any.cpp \
   $(public_api_src_dir)/object/base-handle.cpp \
   $(public_api_src_dir)/object/constrainable.cpp \
   $(public_api_src_dir)/object/handle.cpp \
diff --git a/dali/public-api/object/any.cpp b/dali/public-api/object/any.cpp
new file mode 100644 (file)
index 0000000..d71d575
--- /dev/null
@@ -0,0 +1,87 @@
+//
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Flora License, Version 1.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://floralicense.org/license/
+//
+// 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.
+//
+
+// CLASS HEADER
+#include <dali/public-api/object/any.h>
+
+// EXTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+Any::Any()
+: mContainer( NULL )
+{
+}
+
+Any::~Any()
+{
+  // Call the implementation deletion function, which will invalidate mContainer
+
+  if ( NULL != mContainer )
+  {
+    mContainer->mDeleteFunc( mContainer );
+    mContainer = NULL;
+  }
+}
+
+Any& Any::operator=( const Any& any )
+{
+  if( &any != this )
+  {
+    if( NULL == any.mContainer )
+    {
+      delete mContainer;
+      mContainer = NULL;
+    }
+    else
+    {
+      AnyContainerBase* tmp = mContainer;
+
+      if( NULL != mContainer )
+      {
+        // Check if two Any types have the same type. Avoids assignments of values with different types.
+        if( mContainer->GetType() != any.GetType() )
+        {
+          AssertAlways( "Any::operator=( const Any& Any ). Trying to assign two values with different types." );
+        }
+      }
+
+      // Clone the correct templated object
+      mContainer = any.mContainer->mCloneFunc( *any.mContainer );
+
+      // Deletes previous container.
+      delete tmp;
+    }
+  }
+
+  return *this;
+}
+
+const std::type_info& Any::GetType() const
+{
+  return mContainer ? mContainer->GetType() : typeid( void );
+}
+
+void Any::AssertAlways( const char* assertMessage )
+{
+  DALI_LOG_ERROR_NOFN( assertMessage );
+  throw Dali::DaliException( assertMessage, "" );
+}
+
+
+} //namespace Dali