Wrapping C API Help

54 views
Skip to first unread message

Hubert Cater

unread,
May 22, 2017, 11:04:50 AM5/22/17
to eiffel...@googlegroups.com
I have an API for Steam where I need to make two calls to initialize the Steam application and one to shut it down.

The API for these two calls looks like this:

// SteamAPI_Init must be called before using any other API functions. If it fails, an
// error message will be output to the debugger (or stderr) with further information.
S_API bool S_CALLTYPE SteamAPI_Init();

// SteamAPI_Shutdown should be called during process shutdown if possible.
S_API void S_CALLTYPE SteamAPI_Shutdown();

I unfortunately struggle when it comes to properly wrapping these into a suitable EIFFEL function call and would appreciate any help to have these wrapped properly so that they can be called from the Eiffel side.

Thanks in advance,
Hubert

Virus-free. www.avast.com

Finnian Reilly

unread,
May 22, 2017, 11:39:51 AM5/22/17
to Eiffel Users
Hi Hubert,
you haven't posted the code you have so far, or what compiler error you are getting.

But as a general strategy, have you tried inline calls? See here for an example

http://www.eiffel-loop.com/library/base/runtime/file/names/spec/windows/el_ms_windows_directories.html

This method lets you use include exotic steam macros.

regards
Finnian

Finnian Reilly

unread,
May 22, 2017, 11:43:28 AM5/22/17
to Eiffel Users
And see here for an example of an inline call which returns something
http://www.eiffel-loop.com/library/base/runtime/file/operations/spec/windows/el_win_file_info_c_api.html



Hubert Cater

unread,
May 22, 2017, 12:06:14 PM5/22/17
to eiffel...@googlegroups.com
Hi Finnian,

Thanks for the link and I will take a look and for what I've done so far was to mimic an implementation from another wrapped library I've used in the past which I believe was based on the EWG tool. 

But as I was working through it I had second thoughts if I was even doing the right thing and thought I'd ask for some help/feedback here before I went further.  Part of the reason is I suspect that the method I'm using might be an overcomplication and will also require me to generate a LIB file that I would also need to use.

For example I have the following done on the C side which was to create some glue code:

steam_function_c_glue_code.h

// Wraps call to function 'SteamAPI_Init' in a macro
#include <steam_api.h>
#define function_macro_Steam_API_Init SteamAPI_Init ()
S_API bool S_CALLTYPE function_SteamAPI_Shutdown ();

// Wraps call to function 'SteamAPI_Shutdown' in a macro
#include <steam_api.h>
#define function_macro_Steam_API_Shutdown SteamAPI_Shutdown ()
S_API void S_CALLTYPE function_SteamAPI_Shutdown ();


steam_function_c_glue_code.c

#include <steam_api.h>

// Wraps call to function 'SteamAPI_Shutdown'
// For ise
S_API void S_CALLTYPE function_Steam_API_Shutdown ()
{
    SteamAPI_Shutdown ();
}

#include <steam_api.h>

// Wraps call to function 'SteamAPI_Init'
// For ise
S_API bool function_SteamAPI_Init ()
{
    return SteamAPI_Init ();
}


class STEAM_FUNCTIONS_EXTERNAL

feature
    steam_init_external : BOOLEAN
        external
            "C [macro <steam_function_c_glue_code.h>] (): S_API bool"
        alias
            "function_macro_Steam_API_Init"
        end

    steam_shut_down_external
            -- Address of C function `SDL_Init'
        external
            "C [macro <steam_function_c_glue_code.h>]"
        alias
            "function_macro_Steam_API_Shutdown"
        end
end


Virus-free. www.avast.com

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users+unsubscribe@googlegroups.com.
Visit this group at https://groups.google.com/group/eiffel-users.
For more options, visit https://groups.google.com/d/optout.

Finnian Reilly

unread,
May 22, 2017, 12:16:52 PM5/22/17
to Eiffel Users
Yes, I don't think you need any glue code. Just do something like in this example using "C inline use <myheader.h>"


    frozen c_open_file_read (name: POINTER): NATURAL
           
-- HANDLE WINAPI CreateFile(
           
--     _In_     LPCTSTR               lpFileName,
           
--     _In_     DWORD                 dwDesiredAccess,
           
--     _In_     DWORD                 dwShareMode,
           
--     _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
           
--     _In_     DWORD                 dwCreationDisposition,
           
--     _In_     DWORD                 dwFlagsAndAttributes,
           
--     _In_opt_ HANDLE                hTemplateFile
           
-- );
        external
           
"C inline use <Windows.h>"
       
alias
           
"return (EIF_NATURAL_32) CreateFile ($name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)"
       
end

I just hope the S_CALLTYPE call type doesn't introduce some complications that stop you compiling it in with Eiffel C.

Alexander Kogtenkov

unread,
May 22, 2017, 12:32:06 PM5/22/17
to eiffel...@googlegroups.com
I believe you do not need a wrapper here and can make the calls directly. Something along the following lines should work:

    steam_init_external: BOOLEAN
            -- Call SteamAPI_Init.
        external
            "C inline use <steam_api.h>"
        alias
            "return EIF_TEST(SteamAPI_Init ());"
        end

    steam_shut_down_external
            -- Call SteamAPI_Shutdown.
        external
            "C inline use <steam_api.h>"
        alias
            "SteamAPI_Shutdown ();"
        end

Alexander Kogtenkov


Hubert Cater <hubert....@gmail.com>:

Hubert Cater

unread,
May 22, 2017, 4:36:28 PM5/22/17
to eiffel...@googlegroups.com
Thanks Finnian and Alexander.

I tried the suggested C inline format and everything seems to be set up properly now except for the fact I get a number of C compilation errors.  I'm using Eiffelstudio 6.5 for this project which I believe uses Visual Studio 9 for the C compiler.  It stopped after 100 C compiler errors but here are a few of them and I was hoping either of you might have some further insight?

Error C2057: expected constant expression.                                 steamtypes.h                109,1
Error C2466: cannot allocate an array of constant size 0.               steamtypes.h                109,1
Error C2099: initializer is not a constant.                                      steamtypes.h                125,1

Here is the corresponding file for these three errors.  Unfortunately there are errors such as this with quite a few header files.

#ifndef STEAMTYPES_H
#define STEAMTYPES_H
#ifdef _WIN32
#pragma once
#endif

#define S_CALLTYPE __cdecl

// Steam-specific types. Defined here so this header file can be included in other code bases.
#ifndef WCHARTYPES_H
typedef unsigned char uint8;
#endif

#if defined( __GNUC__ ) && !defined(POSIX)
    #if __GNUC__ < 4
        #error "Steamworks requires GCC 4.X (4.2 or 4.4 have been tested)"
    #endif
    #define POSIX 1
#endif

#if defined(__x86_64__) || defined(_WIN64)
#define X64BITS
#endif

// Make sure VALVE_BIG_ENDIAN gets set on PS3, may already be set previously in Valve internal code.
#if !defined(VALVE_BIG_ENDIAN) && defined(_PS3)
#define VALVE_BIG_ENDIAN
#endif

typedef unsigned char uint8;
typedef signed char int8;

#if defined( _WIN32 )

typedef __int16 int16;
typedef unsigned __int16 uint16;
typedef __int32 int32;
typedef unsigned __int32 uint32;
typedef __int64 int64;
typedef unsigned __int64 uint64;

typedef int64 lint64;
typedef uint64 ulint64;

#ifdef X64BITS
typedef __int64 intp;                // intp is an integer that can accomodate a pointer
typedef unsigned __int64 uintp;        // (ie, sizeof(intp) >= sizeof(int) && sizeof(intp) >= sizeof(void *)
#else
typedef __int32 intp;
typedef unsigned __int32 uintp;
#endif

#else // _WIN32

typedef short int16;
typedef unsigned short uint16;
typedef int int32;
typedef unsigned int uint32;
typedef long long int64;
typedef unsigned long long uint64;

// [u]int64 are actually defined as 'long long' and gcc 64-bit
// doesn't automatically consider them the same as 'long int'.
// Changing the types for [u]int64 is complicated by
// there being many definitions, so we just
// define a 'long int' here and use it in places that would
// otherwise confuse the compiler.
typedef long int lint64;
typedef unsigned long int ulint64;

#ifdef X64BITS
typedef long long intp;
typedef unsigned long long uintp;
#else
typedef int intp;
typedef unsigned int uintp;
#endif

#endif // else _WIN32

#ifdef API_GEN
# define CLANG_ATTR(ATTR) __attribute__((annotate( ATTR )))
#else
# define CLANG_ATTR(ATTR)
#endif

#define METHOD_DESC(DESC) CLANG_ATTR( "desc:" #DESC ";" )
#define IGNOREATTR() CLANG_ATTR( "ignore" )
#define OUT_STRUCT() CLANG_ATTR( "out_struct: ;" )
#define OUT_STRING() CLANG_ATTR( "out_string: ;" )
#define OUT_ARRAY_CALL(COUNTER,FUNCTION,PARAMS) CLANG_ATTR( "out_array_call:" #COUNTER "," #FUNCTION "," #PARAMS ";" )
#define OUT_ARRAY_COUNT(COUNTER, DESC) CLANG_ATTR( "out_array_count:" #COUNTER  ";desc:" #DESC )
#define ARRAY_COUNT(COUNTER) CLANG_ATTR( "array_count:" #COUNTER ";" )
#define ARRAY_COUNT_D(COUNTER, DESC) CLANG_ATTR( "array_count:" #COUNTER ";desc:" #DESC )
#define BUFFER_COUNT(COUNTER) CLANG_ATTR( "buffer_count:" #COUNTER ";" )
#define OUT_BUFFER_COUNT(COUNTER) CLANG_ATTR( "out_buffer_count:" #COUNTER ";" )
#define OUT_STRING_COUNT(COUNTER) CLANG_ATTR( "out_string_count:" #COUNTER ";" )
#define DESC(DESC) CLANG_ATTR("desc:" #DESC ";")
#define CALL_RESULT(RESULT_TYPE) CLANG_ATTR("callresult:" #RESULT_TYPE ";")
#define CALL_BACK(RESULT_TYPE) CLANG_ATTR("callback:" #RESULT_TYPE ";")

const int k_cubSaltSize   = 8;
typedef    uint8 Salt_t[ k_cubSaltSize ];

//-----------------------------------------------------------------------------
// GID (GlobalID) stuff
// This is a globally unique identifier.  It's guaranteed to be unique across all
// racks and servers for as long as a given universe persists.
//-----------------------------------------------------------------------------
// NOTE: for GID parsing/rendering and other utils, see gid.h
typedef uint64 GID_t;

const GID_t k_GIDNil = 0xffffffffffffffffull;

// For convenience, we define a number of types that are just new names for GIDs
typedef uint64 JobID_t;            // Each Job has a unique ID
typedef GID_t TxnID_t;            // Each financial transaction has a unique ID

const GID_t k_TxnIDNil = k_GIDNil;
const GID_t k_TxnIDUnknown = 0;

const JobID_t k_JobIDNil = 0xffffffffffffffffull;

// this is baked into client messages and interfaces as an int,
// make sure we never break this.
typedef uint32 PackageId_t;
const PackageId_t k_uPackageIdFreeSub = 0x0;
const PackageId_t k_uPackageIdInvalid = 0xFFFFFFFF;

typedef uint32 BundleId_t;
const BundleId_t k_uBundleIdInvalid = 0;

// this is baked into client messages and interfaces as an int,
// make sure we never break this.
typedef uint32 AppId_t;
const AppId_t k_uAppIdInvalid = 0x0;

typedef uint64 AssetClassId_t;
const AssetClassId_t k_ulAssetClassIdInvalid = 0x0;

typedef uint32 PhysicalItemId_t;
const PhysicalItemId_t k_uPhysicalItemIdInvalid = 0x0;


// this is baked into client messages and interfaces as an int,
// make sure we never break this.  AppIds and DepotIDs also presently
// share the same namespace, but since we'd like to change that in the future
// I've defined it seperately here.
typedef uint32 DepotId_t;
const DepotId_t k_uDepotIdInvalid = 0x0;

// RTime32
// We use this 32 bit time representing real world time.
// It offers 1 second resolution beginning on January 1, 1970 (Unix time)
typedef uint32 RTime32;

typedef uint32 CellID_t;
const CellID_t k_uCellIDInvalid = 0xFFFFFFFF;

// handle to a Steam API call
typedef uint64 SteamAPICall_t;
const SteamAPICall_t k_uAPICallInvalid = 0x0;

typedef uint32 AccountID_t;

typedef uint32 PartnerId_t;
const PartnerId_t k_uPartnerIdInvalid = 0;

// ID for a depot content manifest
typedef uint64 ManifestId_t;
const ManifestId_t k_uManifestIdInvalid = 0;



#endif // STEAMTYPES_H



Virus-free. www.avast.com

hcater

unread,
May 22, 2017, 5:07:23 PM5/22/17
to Eiffel Users, kwa...@mail.ru
I just tried it with EiffelStudio 16.05 and received the same set of C compilation errors.  To confirm I'm on Windows 10 64-bit and this latest test was with the Visual Studio 11 C compiler.

Hubert

Hubert Cater

unread,
May 23, 2017, 12:43:59 AM5/23/17
to eiffel...@googlegroups.com
Due to all the C compilation errors I've reduced the steam_api.h file to the following as these are the only two calls I need wrapped.  Assuming the following is correct this is what I have:

#ifndef STEAM_API_H
#define STEAM_API_H

#ifdef _WIN32
#pragma once
#endif

#define S_CALLTYPE __cdecl
#define S_API extern "C" __declspec( dllimport )

S_API bool S_CALLTYPE SteamAPI_Init();
S_API void S_CALLTYPE SteamAPI_Shutdown();

#endif // STEAM_API_H


As C/C++ is not my forte I'm obviously missing something as the C compiler error I'm getting on the Eiffel side when I try and include the steam_api.h and steam_api.lib files into my project is the following:

sdk\public\steam\steam_api.h(11) : error C2059: syntax error : 'string'
sdk\public\steam\steam_api.h(12) : error C2059: syntax error : 'string'


I'm a bit stuck on this as I'm unfortunately not seeing what is wrong with these two lines:

S_API bool S_CALLTYPE SteamAPI_Init();
S_API void S_CALLTYPE SteamAPI_Shutdown();

Any help here would again be greatly appreciated, thanks,
Hubert

Virus-free. www.avast.com

--

Alexander Kogtenkov

unread,
May 23, 2017, 4:09:36 AM5/23/17
to eiffel...@googlegroups.com
It looks like the API uses C++ rather than C, so you need to use external C++, e.g.:

    steam_init: BOOLEAN
            -- Call SteamAPI_Init.
        external
            "C++ inline use <steam_api.h>"

        alias
            "return EIF_TEST(SteamAPI_Init ());"
        end

    steam_shutdown
            -- Call SteamAPI_Shutdown.
        external
            "C++ inline use <steam_api.h>"

        alias
            "SteamAPI_Shutdown ();"
        end

Alexander Kogtenkov

Hubert Cater <hubert....@gmail.com>:

To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.

Hubert Cater

unread,
May 23, 2017, 10:00:33 AM5/23/17
to eiffel...@googlegroups.com
Thank you very much and this is exactly what I needed as it is all working now on my end, much appreciated.

Hubert Cater <hubert....@gmail.com>:

To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages