Newer
Older
Import / applications / HighwayDash / ports / Framework / Object.cpp
//
//  Object.cpp
//  BlockyFroggy
//
//  Created by John Ryland on 26/5/17.
//  Copyright © 2017 John Ryland. All rights reserved.
//

#include "Object.h"
#include "Common.h"
#include "Utilities.h"
#include <string>


struct PlainOldData
{
  int x, y, z;
};

class Visitor
{
};

template <typename T>
void visit(T& obj, Visitor& visitor);

// This can be hidden in a c++ file
template <>
void visit(PlainOldData& obj, Visitor& visitor)
{
  printf("blah\n");
}


////////////////////////////////////////////////////
////////////// START OF UNIT TESTS /////////////////
////////////////////////////////////////////////////
#if ENABLE_UNIT_TESTS


DECLARE_OBJECT_TYPE(BaseObjectSize)
END_OBJECT_TYPE(BaseObjectSize)


DECLARE_UNIT_TEST(unitTestObjectSizeOverhead)
{
  size_t siz = sizeof(BaseObjectSize);
  CHECK(siz <= 4);
}


DECLARE_OBJECT_TYPE(TestObject)
 DECLARE_MEMBER(int, m_val, 5)
 BEGIN_MEMBER_FUNCTION(int, getCount, (int x, int y))
   return 6;
 END_MEMBER_FUNCTION(getCount)
END_OBJECT_TYPE(TestObject)



// Capture enums and their values
// Somehow need to capture this in static data when we decorate our classes with macros or however
// Alternative:
//       https://github.com/TNG/boost-python-examples/blob/master/04-ClassMembers/member.cpp
//           Something like:
//                class Blah { int member;  void doSomething(); };
//                void doBindings() {
//                  Capture<Blah>().add_member("member", member).add_function("doSomething", doSomething);
//                }
//                // Something like that. also add_property(getter, setter)
// Similar:
//   struct Color { int red, green, blue; };
//   ThorsAnvil_MakeTrait(Color, red, green, blue);
// ThorsAnvil can do enums and other stuff
/*
struct Color { int red, green, blue; };

template<typename T>
class Traits
{
};

template<>
class Traits<Color>
{
    public:
      //static constexpr TraitType type = TraitType::Map;
      using Members = std::tuple<
        std::pair<char const*, decltype(&Color::red)>,
        std::pair<char const*, decltype(&Color::green)>,
        std::pair<char const*, decltype(&Color::blue)>>;
        static Members const& getMembers()
        {
            static constexpr Members members{
                { "red", &Color::red },
                { "green", &Color::green },
                { "blue", &Color::blue } };
            return members;
        }
};
*/

class TypeInformation
{
public:
  enum MemberType {
    Variable,
    Function
  };
  enum Qualifiers {
    None,
    Const
  };
  struct VariableDetails {
    Qualifiers       m_qualifiers;
    TypeInformation* m_type;
    std::string      m_name;
  };
  MemberType         m_memberType;
  Qualifiers         m_qualifiers;
  TypeInformation*   m_type;       // return type or variable type
  std::string        m_name;       // function or variable name
  std::vector<VariableDetails> m_functionArguments;

  //! @todo see Lua bindings
  void Call(void* a__this, std::vector<void*> a_args);
};

#include <typeinfo>       // operator typeid
#include <typeindex>      // std::type_index

class TypeInfoParameter : public TypeInformation
{
public:
  Qualifiers       m_qualifiers;
  TypeInformation* m_type;
  std::string      m_name;
//  Qualifiers
};

class TypeInfoFunction : public TypeInformation
{
public:
  TypeInfoFunction(TypeInformation a_retType, std::string a_name, TypeInfoParameter* a_parameters);
  TypeInformation* m_returnType;
  std::string m_name;
  std::vector<TypeInfoParameter> m_parameters;
};




// A simple server in the internet domain using TCP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>


class ClientSession
{
public:
  virtual ~ClientSession() {}
  virtual bool update() = 0;
};


typedef std::function<std::shared_ptr<ClientSession>(int a_socket)> ClientSessionFactory;


class DebugSocket
{
public:
  DebugSocket(int a_port, ClientSessionFactory a_clientFactoryMethod)
  {
    m_clientFactory = a_clientFactoryMethod;
    m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (m_sockfd >= 0) {
      sockaddr_in serv_addr = { 0 };
      //int option = 1;
      //setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
      //bzero((char *)&serv_addr, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_addr.s_addr = INADDR_ANY;
      serv_addr.sin_port = htons(a_port);
      if (bind(m_sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) >= 0) {
        listen(m_sockfd, 5);
        m_thread = std::thread(&DebugSocket::run, this);
        return;
      }
      close(m_sockfd);
    }
    perror("ERROR opening/binding socket");
    m_sockfd = -1;
  }
  
  ~DebugSocket()
  {
    m_shuttingDown = true;
    if (m_thread.joinable()) {
      m_thread.join();
    }
    if (m_sockfd != -1) {
      close(m_sockfd);
    }
  }
  
  int acceptNewClient(long timeout)
  {
    sockaddr_in cli_addr;
    socklen_t clilen = sizeof(cli_addr);
    timeval tv = { timeout, 0 };
    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(m_sockfd, &rfds);
    if (select(m_sockfd+1, &rfds, nullptr, nullptr, &tv) > 0) {
      return accept(m_sockfd, (struct sockaddr *) &cli_addr, &clilen);
    }
    return -1; // timeout
  }

  void run()
  {
    std::vector<std::thread> clients;
    while (!m_shuttingDown) {
      int newSock = acceptNewClient(1);
      if (newSock >= 0) {
        clients.emplace_back(&DebugSocket::clientRun, this, newSock);
      }
      // this grows with time, when a client goes away, the std::thread object hangs around but isn't 'leaked'
      // if expecting a large number of connects and disconnects from many clients, then removing
      // them from the vector as they close would be a good idea
    }
    for (auto& client : clients) {
      if (client.joinable()) {
        client.join();
      }
    }
  }
  
  void clientRun(int newsockfd)
  {
    auto client = m_clientFactory(newsockfd);
    while (!m_shuttingDown) {
      if (!client->update()) {
        break;
      }
    }
    close(newsockfd);
  }

private:
  int m_sockfd;
  std::thread m_thread;
  std::atomic<bool> m_shuttingDown = { false };
  ClientSessionFactory m_clientFactory;
};



/*

Idea:
  1st stage is to be able to in unit tests, print an object tree and
  dump the type info. Also to modify properties in none tightly bound ways.

  2st stage is to be able to connect via a debug port a terminal (bonjour?)
  in the terminal you can type commands such as to print the object tree,
  you can then introspect the registered types with commands too
  then with commands create objects of those types in the object tree
  You can modify properties of objects in the object tree too
  
  3nd stage is a GUI tool to connect to the debug port which can
  do those things

*/



/*
template <typename T>
class Object
{
public:
   // Perhaps has visitor for serialization / deserialization
   // Tie it together with the language bindings - lua / python / javascript
  static int blah;  // Now there is one of these for each type T
  void func() { static int blah2 = 0; } // ditto when func is called
  static struct blah3 { blah3()
  {
  static int blah4 = 0;
  } } m_blah5; // ditto, no need to call anything

};
*/



#include "Object.h"

// Example header declarations

struct Foo;

struct ref(RefClass)
{
  void init(int x, int y, int z);
  void foo();
  void bar();

  // Perhaps this is how to do properties - a function that returns a reference, therefore can assign to it or read it
  Foo& value();
};


class Foo
{
public:
  Foo(){}
  int x, y;
};


struct ref_inherit(SubRefClass,RefClass)
{
  void init(int x);
  void blah();
};



// Demonstration usage:

// slicing test
struct A { int x; };
struct B : public A { B(int a, int b) : A{a}, y(b) {} int y; };


constexpr unsigned int str2int(const char* str, int h = 0)
{
    return !str[h] ? 5381 : (str2int(str, h+1) * 33) ^ str[h];
}


template <typename T>
void SetSockOpt(int a_socket, int a_option, T val)
{
  setsockopt(a_socket, SOL_SOCKET, a_option, (void *)&val, sizeof(T));
}


class Client : public ClientSession
{
public:
  Client(int a_socket) : m_socket(a_socket)
  {
    SetSockOpt<int>(a_socket, SO_NOSIGPIPE, 1);
    sendf("> ");
  }
  
  void recurseDumpChildren(std::shared_ptr<BasePimpl> a_child)
  {
    sendf("children:");
    for (auto child : a_child.get()->m_children)
      sendf(" %x %s", child.get(), child.get()->dynamicTypeName().c_str() );
    sendf("\n");
    for (auto child : a_child.get()->m_children)
      recurseDumpChildren(child);
  }

  bool update() override
  {
    char buffer[256];
    bzero(buffer, 256);
    ssize_t n = read(m_socket, buffer, sizeof(buffer));
    if (n < 0) {
      perror("ERROR reading from socket");
      return false;
    }

    switch (str2int(buffer))
    {
      case str2int("\n"):
        break;
      case str2int("dump types\n"):
        sendf("dumping types ...\n");
        for (auto i : RegisteredTypes())
        {
          sendf("type: %s (child of: %s)\n", i.second.m_typeName.c_str(), i.second.m_parentTypeName.c_str());
          sendf("funcs:");
          for (auto f : i.second.m_functions)
            sendf(" %s", f.c_str());
          sendf("\n");
        }
        break;
      case str2int("dump objecttree\n"):
        sendf("dumping object tree ...\n");
        sendf("root: %x\n", getRootObject().get());
        recurseDumpChildren(getRootObject());
        break;
      case str2int("get\n"):
        sendf("getting ...\n");
        break;
      case str2int("set\n"):
        sendf("setting ...\n");
        break;
      case str2int("new\n"):
        sendf("newing ...\n");
        break;
      case str2int("exec\n"):
        sendf("execing ...\n");
        break;
      case str2int("help\n"):
        sendf("commands:\n\n dump [types | objecttree] | get | set | new | exec | help\n\n");
        break;
      default:
        sendf("invalid command, enter 'help' for command help\n");
        break;
    }
    return sendf("> ");
  }

private:
  int m_socket;
  
  template <size_t N>
  bool sendf(const char (&a_fmt)[N])
  {
    size_t siz = snprintf(nullptr, 0, "%s", a_fmt);
    char buf[siz+2];
    snprintf(buf, siz+1, "%s", a_fmt);
    buf[siz+1] = 0;
    ssize_t n = write(m_socket, buf, siz+1);
    if (n < 0) {
      perror("ERROR writing to socket");
      return false;
    }
    return true;
  }

  template <size_t N, typename ...Ts>
  bool sendf(const char (&a_fmt)[N], Ts... args)
  {
    size_t siz = snprintf(nullptr, 0, a_fmt, args...);
    char buf[siz+2];
    snprintf(buf, siz+1, a_fmt, args...);
    buf[siz+1] = 0;
    ssize_t n = write(m_socket, buf, siz+1);
    if (n < 0) {
      perror("ERROR writing to socket");
      return false;
    }
    return true;
  }
};


void test()
{
  RefClass test0 = RefClass::create(1,2,3);
  SubRefClass test1 = SubRefClass::createChild(test0, 5);
  test1.blah();
  test1.foo();
  RefClass test2 = test1;
  test2.bar();
  test2.foo();
  test1.blah();
  test1.foo();
  RefClass test3 = test2.deepCopy();
  test3.bar();
  test3.foo(); // no slicing
  test1.blah();
  test1.foo();
  
  test0.value();
  
  B b1(1, 2);
  B b2(3, 4);
  A& a = b2;
  a = b1;
  // b2 is now sliced, contains  { 1, 4 }  eg: half of b1 and half of b2
//  int x = 0;

  // Spawn a debugger socket
  DebugSocket socket(1090, [](int a_socket) { return std::make_shared<Client>(a_socket); }); //! @todo this leaks memory
  std::this_thread::sleep_for(std::chrono::seconds(30));
}

// Implementation details:

//! @todo to actually inherit, you need to have access to the details of the parent pimpl definition
// That means that 3rd parties wouldn't be able to effectively inherit from it if you only provided
// headers and libs without source code. If on the other hard pimpls weren't being used in a normal
// C++ manner, then you could inherit. However it doesn't prevent composition from being used which
// may be preferable anyway.


// Need some way to make generating this boilerplate code better, both for the pimpl and the pass through code

#include <iostream>
#undef BARK
#define BARK std::cout << "static: " << staticTypeName() << " dynamic: " << dynamicTypeName() << "::" << __FUNCTION__ << std::endl

template<> struct RefClass::BaseType::Pimpl : public PimplT<RefClass>
{
  virtual void init(int a_x, int y, int z) { x = a_x; BARK; printf("x : %i\n", x+=3); }
  virtual void foo() { BARK; printf("x : %i\n", x+=3); }
  virtual void bar() { BARK; printf("x : %i\n", x+=3); }
  virtual Foo& value() { BARK; return f; }
  Foo f;
  int x = 100;
};

void RefClass::init(int a_x, int y, int z) { return forward(&Pimpl::init, a_x, y, z); }
void RefClass::foo()                       { return forward(&Pimpl::foo); }
void RefClass::bar()                       { return forward(&Pimpl::bar); }
Foo& RefClass::value()                     { return forward(&Pimpl::value); }

template<> MetaData RefClass::BaseType::getMeta()
{
  MetaData d;
  d.addFunction("void", "init", "int", "a_x", "int", "y", "int", "z");
  d.addFunction("void", "foo");
  d.addFunction("void", "bar");
  d.addFunction("Foo&", "value");
  return d;
}

/*
void RefClass::metaData()
{
  // need type info too
  "init"  -> &Pimpl::init
  "foo"   -> &Pimpl::foo
  "bar"   -> &Pimpl::bar
  "value" -> &Pimpl::value
  // Perhaps need serialize/deserialize on calling these too
  // Need a generic serialize/deserialize on objects to do this? From scripting point of view, need text<->object
  // but might be a binary way to do it too if you have all the type info
};

perhaps at some point it is worth doing code gen for it all

*/



template<> struct SubRefClass::BaseType::Pimpl : public PimplT<SubRefClass>
{
  virtual void init(int a_x) { ParentType::init(a_x, 3, 4); BARK; printf("x : %i\n", x+=3); }
  virtual void foo() { BARK; printf("x : %i\n", x+=3); }
  virtual void blah() { BARK; printf("x : %i\n", x+=3); }
  int y = 200;
};

void SubRefClass::init(int a_x) { return forward(&Pimpl::init, a_x); }
void SubRefClass::blah()        { return forward(&Pimpl::blah); }

template<> MetaData SubRefClass::BaseType::getMeta()
{
  MetaData d;
  return d;
}

template SubRefClass::TypeRegistrar SubRefClass::s_typeRegistrator;



// This doesn't need to be called, but needs to exist
// Types need to be registered here if they need to be access via introspection
// Would be better this wasn't needed
void RefObject::registerTypeFactories()
{
  RefObject::s_typeRegistrator = RefObject::s_typeRegistrator;
  RefClass::s_typeRegistrator.func("bar", &RefClass::bar).func("foo", &RefClass::foo);
  //SubRefClass::s_typeRegistrator;
}


class Reg : public RefObject
{
public:
  Reg() { RefObject::registerTypeFactories(); }
  static Reg s_reg;
};
Reg Reg::s_reg;


// Forward declare
class FooBar;
// Now use to define function, don't need to use * or & and this compiles
FooBar someFunc(FooBar arg);
// Therefore could just about not have headers include other headers - much faster compile times


void visitObj()
{
  PlainOldData obj;
  Visitor visitor;
  // Compiles but then linker error if haven't defined in a cpp file the implementation for
  // the expanded template with those types (this has been done in Object.cpp)
  visit(obj, visitor);
}


// Unit Testing
DECLARE_UNIT_TEST(unitTestObject)
{
  test();
  visitObj();

  TestObject obj;
  obj.getCount(1, 2);
  obj.dispatch();
  CHECK(true);
}


#endif // ENABLE_UNIT_TESTS