- PR -

C の localtime() の引数は混乱を招く。

投稿者投稿内容
ほむら
ぬし
会議室デビュー日: 2003/02/28
投稿数: 583
お住まい・勤務地: 東京都
投稿日時: 2004-09-29 21:58
ども、ほむらです。
気づいたら長文になっていました。。。
--------
コブラ氏
>こうしないのは、コンパイラのパディングを予想してという理由だけなのでしょうか?
>それとも他に何か私の思いもよらない理由があるのでしょうか?

そんなに深い理由があるわけではありませんが
今は昔、僕がまだプログラムをしていた時代。。。
データを固定長のバイナリとして持っていたわけです。
これを読み込むときに便利なのが構造体。
でもってちょっとだけ違う構造体もあるわけで。。。。

コード:
 typedef struct tagLocation
 {
   int x;
   int y;
   int width;
   int height;
 } Location;

 typedef struct tagSprite
 {
   Location locate;
   long kindof;
   long priority;
 } Sprite;
 
 typedef struct tagSprite1
 {
  Sprite spr;
  int pattern_total;
 } Sprite1;
 
 typedef struct tagSprite2
 {
  Sprite spr;
  int pattern_total;
  int frame_interval;
 } Sprite2;
 
 int
  Sprite_conflict(Sprite s1, Sprite s2)
 {
 	Location area;
 	get_intersect(&area, (Location *)&s1, (Location *)&s2);
 	return 0;
 }
 int get_intersect(Location *dst, const Location *src1, const Location *src2){ return 1; }
 
 でもって、

 void
  sample(void)
 {
 	Sprite1 sprite1;
 	Sprite2 sprite2;
 
 	Sprite_conflict((Sprite)sprite1.spr, (Sprite)sprite2.spr);
 }

 みたいに無理矢理なキャストとか
 とか
 void
  sample2(void)
  {
 	Sprite1 sprite1;
 	Sprite2 sprite2;
    Sprite all[]={ (Sprite *)&sprite1.spr, (Sprite *)&sprite1.spr}
  }
  みたいに
  インターフェイス的なことができます。


これがなぜ安全なのかというのは
メモリダンプ(orファイル出力 バイナリエディタで閲覧等)を見てもらうと良いと思います。
intel系と指定した理由も同じくです。

全く異なる型への安全な(?)キャストができるから継承っぽいことと表現しただけです
まぁたんに無理矢理出しただけの例ですのであまり気にしなくてもいいと思います。
便利だけど破綻しやい危険な使い方であるのも確かですから。。

知識として持ってるだけで十分かと。。。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-09-29 22:41
 コブラさんって、もともとWindows系なのかな?Windowsのライブラリって、APIの中で領域確保して、「呼び出し側で解放してくれ」ってのが多いですね。

対して、POSIX準拠?UNIX系には、そういう関数は滅多にないような?
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2004-09-30 02:23
引用:

コブラさんの書き込み (2004-09-29 13:09) より:
localtime() そっくりの結果を返す localtime_s() っちゅぅ関数を作りました。
--
bool localtime_s(struct tm *tm, time_t t)
--
、、ぶっちゃけ曜日が一日狂う時が「たまに」あるんですが (プ
今、原因を調べてます。


全然良く分からずに書いてるんで思いっきり外してるかもしれませんが、

struct tm *localtime_s(time_t timer)
{
 return localtime(&timer);
}
とか
struct tm localtime_s(time_t timer)
{
 return *localtime(&timer);
}
したらいいとか、そういう話ではない?
ナキヲ
常連さん
会議室デビュー日: 2003/08/22
投稿数: 32
お住まい・勤務地: 京都・自宅から勤務地まで自転車で40分
投稿日時: 2004-09-30 23:25
引用:

そういう話ではない?



そういう話でいいと思います。
私もやるならこれですね。
時間の計算の実装を自分でやることはこのスレッドの目的ではないですから。。
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-10-01 13:32
いやぁ〜、いっぺん unix-C やって、そっから LPCTSTR だらけの VC++ に移って、
そっからまた gcc に・・・ 一度身に着いたモノを捨てんと、また同じ失敗を
やらかしてしまいます(笑) > Jitta氏

なちゃ氏:
勿論、考えましたそれ。
何ちゅぅか、まぁせっかく「自分用の関数を作れ」とおっしゃってるのだから(笑)


ほむら氏のソースを拡張してます。
ちょっと見ただけでは、

「こ、この構造体の入れ子の深さは・・・」

ちょっとレベルが高過ぎて躊躇してしまいますので、実際に動きを確かめながら
理解するようにします。

ただ、Location をスーパークラスと見立てて、スーパークラス Location を
呼び出す 子クラス Sprite, 子クラス Sprite を呼び出す孫クラス Sprite1, Sprite2

こう考えると、確かに Sprite1 から スーパークラスの Location のメンバー
にアクセスできますね。つまり、自分自身の実装以外に、親クラスの属性・メソッドを
呼び出せる、、継承できると認識しました。

後、私自身が感じた「肝」の部分は、 get_intersect() の第二引数以降で、
定義した型を強制的にキャストで型変換しておられます。

ここが、私がかつて見たことが無い事をしておられるので非常に興味深い。
っちゅぅか、初めて見た時「こんな事できんのか?」と思いましたが、
実際は、コンパイラのキャストの Warning 2個ぐらいだけでしたね。。。(笑)

こういう発想・実装ができるようになりたいもんです。
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-10-04 16:31
いやー、「どうやってインターフェースもどきを実現しようか・・・」と、かなり考えて
やっと、それらしきモノになりました。まぁ、あくまで「もどき」ですが。。。

これまで、ここで学んできた事を、自分なりに応用して造ってますが、これを自分で
コントロールするのは、私にはまだ荷が重い (プ

ソースは二つに分かれてます。

super.c
コード:
#include <stdio.h>

#include <stdlib.h>

// バーチャルの実体
void super_method1()
{
printf("実体1\n");
}

// バーチャルの実体
void super_method2()
{
printf("実体2\n");
}

// 似非スーパークラス
struct Super
{
int x;
int y;

int width;
int height;
};

// 似非バーチャル
struct Virtual
{
struct Super class;
void (*substance1)();
void (*substance2)();

};

// 似非インターフェース
struct Interface
{
// 似非インターフェース用スタブ
void (*interface)();
struct Virtual virtual;
};

// クラス見立てのただの構造体
struct Super class;
struct Interface interface;
//struct Virtual *virtual = (struct Virtual *)&interface.virtual;
// 同じクラスでありながら、別々のインスタンスに同じメンバー
struct Virtual *virtual1 = (struct Virtual *)&interface.virtual;
struct Virtual *virtual2 = (struct Virtual *)&interface.virtual;

void super_sub()
{
// インターフェース実装のプロトタイプ宣言は重要
void sub_method1(void);

interface.interface = (void *)&sub_method1;
interface.interface();
}

/*
void sub_method1()
{
printf("所詮、サブ・クラス側でオーバーライドされる運命\n");
}
*/

void super()
{
virtual1->substance1 = (void *)&super_method1;
virtual2->substance2 = (void *)&super_method2;

virtual1->substance1();
virtual2->substance2();

super_sub();
}

// サブ・クラスに提供する部分
struct Instance
{
struct Interface newface;
volatile unsigned long *Object;
void (*method)();
unsigned char String[1];

int x;
int y;

int width;
int height;
};

struct Instance *package;




以上が呼び出され側。

呼び出し側
sub.c
コード:
#include <memory.h>

#include "super.c" // import super.c

// インターフェースの実装はサブ・クラスで
void sub_method1()
{
printf("実体3\n");
}

void *new(struct Instance* this)
{
this = (struct Instance *)malloc(sizeof(struct Instance));
if(this){
(*this).method = (void *)&super;
}
printf("1>>%p, %p\n", this, &super);

return(this);
}

void delete(struct Instance* this)
{
if(this) free(this);
}

int main()
{
struct Instance* sub_class = (struct Instance *)0;

sub_class = (struct Instance *)new(package);
printf("2>>>>%p\n", sub_class);
if(sub_class) (*sub_class).method();
delete(sub_class);

return(0);
}



コンパイル:
   gcc -o sub -O sub.c

スーパークラスのメソッドとインターフェースの実装部分の違いが明らかになるように
しました。。。と思たら、これメンバーに対する具体的な値やのぅて、 struct に
対して malloc しとるからダブルポインタはお門違いでした。

 又しても Solaris の「堅さ(?)」にやられてしまいました。 Linux ではメモリアクセス
違反が発生・・・Solaris では何故か動きます。

修正しました。

Warning は取れました。
しかし、オーバーライドはどうしても実現できない。


[ メッセージ編集済み 編集者: コブラ 編集日時 2004-11-11 14:47 ]

[ メッセージ編集済み 編集者: コブラ 編集日時 2004-11-11 14:50 ]
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-10-04 16:51
localtime(); 自作するのに、ここまでやらなアカンとは・・・
ぶっちゃけ、前の localtime_s() の

if(d == 0.000) d = 1.000; これは、

if(d > 0.000 && d < 1.000) d = 1.000; こう変えんとマトモに動きませんな(笑)
後、色々変更。また変更するかも (プ


コード:
#include <stdlib.h>

#include <math.h>
#include <time.h>
#include <sys/time.h>

//#define _ISOC99_SOURCE

#define DAYS (60 * 60 * 24)
#define NORM (DAYS * 365)
#define LEAP (DAYS * 366)
#define EPOC 1970

typedef unsigned int bool;

static const bool TRUE = 1;
static const bool FALSE= 0;

short ttl[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
short ept[13] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
short mon[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
enum date {Thu, Fri, Sat, Sun, Mon, Tue, Wed};

void disp(struct tm *);

///////////////////////////////////////////////////////////////////
// localtime_s()
// 機能 : 年・月・日・曜日・時・分・秒・通算経過日数・夏時間を出力
//
// 入力 :
// 第一引数: *tm
// 型: tm 構造体実体のアドレスへのポインター
// 第二引数: t
// 型: unsigned long
//
// 出力 : *tm
// 型: tm 構造体実体のアドレスへのポインター
//
// 戻り値 : 論理型
// TRUE : 1
// FALSE: 0
//
///////////////////////////////////////////////////////////////////
bool localtime_s(struct tm *tm, time_t t)
{
enum date wday = Thu;
time_t now;
unsigned long tmp = 0L, temp=0L;
unsigned int y, month, h, mn, s, c, zg, cur;
double d, days, gap;
unsigned int leap = 0;
short i=0;
char *TZ, tz[4];
char flg = '0';

/* 初期化 */
y = month = h = mn = s = c = zg = cur = 0;
d = days = 0.0000;

/* 1970年1月1日から指定年度まで */
for(tmp = 0; tmp <= t;){
c = y + EPOC;


/* 閏年カウント */
if(!(c % 4) && ((c % 100) || !(c % 400))){
leap++;

/* 指定時刻の前年度迄でループを止める */
if(tmp + NORM >= t){
break;
}

tmp += LEAP;
}
else {

/* 指定時刻の前年度迄でループを止める */
if(tmp + NORM >= t){
break;
}

tmp += NORM;
}

y++;
}

y = c;

if(!(y % 4) && ((y % 100) || !(y % 400))){
flg = '1';
}

/* 指定年度分のみの秒数 */
gap = (double)t - tmp;

/* 1970年1月1日は木曜日(wday[0..3] スキップ)開始 */
temp = Sun;
temp += t / DAYS;
if(!(y % 4) && ((y % 100) || !(y % 400))) temp++;
/* 1週間で割った余りの曜日 */
temp %= 7;

if(gap > 0) days = gap / DAYS;
else days = 1;

for(i = 1; i <= 12; i++){
if(flg == '0'){
/* 指定年度分の総秒数が1月以降かどうか */
if(gap > (ttl[1] * DAYS)){
/* 月毎の累積秒数が指定秒数を上回らないように */
if(gap <= (ttl[i + 1] * DAYS)){
/* 先月に一月足したら今月 */
month = i + 1;
/* 指定秒数を日数に変換、前月までの累積日数
を引いたら今月分の日付 */
d = (double)(gap / DAYS) - ttl[i];

/* 0日は1日 */
if(d > 0.000 && d < 1.000) d = 1.000;
else d++;

temp++;

break;
}
}
/* 年度が変わって一ヶ月経ってない */
else {
/* 31日以内では差分日数がそのまま日付になる */
if(gap > 0.000){
d = (double)(gap / DAYS);
if(d > 0.000 && d < 1.000){
d = 1.000;
}
else d++;

temp++;
}
else d = 1.000; // 0日は1日
/* 今月は 1月確定 */
month = i;

break;
}
}
else {
/* 指定年度分の総秒数が1月以降かどうか */
if(gap > (ept[1] * DAYS)){
/* 月毎の累積秒数が指定秒数を上回らないように */
if(gap <= (ept[i + 1] * DAYS)){
/* 先月に一月足したら今月 */
month = i + 1;
/* 指定秒数を日数に変換、前月までの累積日数
を引いたら今月分の日付 */
d = (double)(gap / DAYS) - ttl[i];

/* 0日は1日 */
if(d > 0.000 && d < 1.000) d = 1.000;
else d++;

temp++;

break;
}
}
/* 年度が変わって一ヶ月経ってない */
else {
/* 31日以内では差分日数がそのまま日付になる */
if(gap > 0.000){
d = (double)(gap / DAYS);
if(d > 0.000 && d < 1.000){
d = 1.000;
}
else d++;

temp++;
}
else d = 1.000; // 0日は1日
/* 今月は 1月確定 */
month = i;

break;
}
}
}

/* 指定時刻時・分・秒の割出し */
h = t % DAYS;
mn = h % (60 * 60);
s = mn % 60;

h /= (60 * 60);
mn /= 60;

/* タイムゾーン増減 */
TZ = (char *)getenv("TZ");
if(TZ != 0){
memset(tz, 0x00, sizeof(tz));
strncpy(tz, (TZ + 3), 3);
}
else tz[1] = '0';

/* atoi() */
for(i=1, zg=0; i <= strlen(&tz[1]); i++){
zg *= 10;
zg += (tz[i] - 0x30);
}

/* ローカル化して24時間を超えるようなら、
日付、累積日数、曜日全てが増分 */
if(h + zg >= 24){
d = d + 1.000;
/* 月末調整 */
if(d > mon[month] + 1){
month++;
d = 1.000;
}

/* 年末調整 */
if(month > 12){
y++;
month = 1;
days = 0;
}
else {
days++;
}

temp++;
if(temp > 6) temp %= 7;
}
else if(temp > 6) temp %= 7;

/*
time(&now);
cur = now / NORM;
cur += 1970;

// ローカライズタイム
if(cur == y) h += zg;
*/

/* 24時間サイクル化 */
h += zg;
if(h >= 24){
h %= 24;
}

wday = temp;

/* 第一引数構造体へ代入 */
tm->tm_sec = s;
tm->tm_min = mn;
tm->tm_hour = h;
tm->tm_mday = (int)d;
tm->tm_mon = month;
tm->tm_year = y;
tm->tm_wday = wday;
tm->tm_yday = (int)days;
tm->tm_isdst = 0;

/* 成功 */
return(TRUE);
}

int main()
{
time_t t;
struct tm tm;

time(&t);

if(localtime_s(&tm, t)){
disp(&tm);
}

return(1);
}

void disp(struct tm *tm)
{
static unsigned char *date[] = {"日", "月", "火", "水", "木", "金", "土" };
char *Z;
unsigned char buf[7];

memset(buf, 0x00, sizeof(buf));

Z = (char *)getenv("TZ");
if(Z != 0){
memset(buf, 0x00, sizeof(buf));
strncpy(buf, Z, 3);
}

printf("%d年 ", tm->tm_year);
printf("%d月 ", tm->tm_mon);
printf("%d日 ", tm->tm_mday);
printf("%s曜日 ", date[tm->tm_wday]);
printf("%02d:", tm->tm_hour);
printf("%02d:", tm->tm_min);
printf("%02d", tm->tm_sec);
printf(" %d日目 ", tm->tm_yday);

printf("%d ", tm->tm_isdst);
printf("%s\n", buf);
}



[ メッセージ編集済み 編集者: コブラ 編集日時 2005-05-02 14:07 ]
コブラ
ぬし
会議室デビュー日: 2003/07/18
投稿数: 1038
お住まい・勤務地: 神奈川
投稿日時: 2004-11-11 15:04
ドメインと役割の対応を見直しました。

super.c
コード:
#include <stdio.h>

#include <stdlib.h>

#define MAX_METHODS 2048

// バーチャルの実体
void super_method1()
{
printf("実体1\n");
}

// バーチャルの実体
void super_method2()
{
printf("実体2\n");
}

// 似非スーパークラス
struct Super {
int x;
int y;

int width;
int height;
};

// 似非バーチャル
struct Virtual {
struct Super class;
//なんぼでも(大袈裟)メソッド追加できる
void (*substance[MAX_METHODS])(void);
};

// 似非インターフェース
struct Interface {
// 似非インターフェース用スタブ
void (*interface[MAX_METHODS])(void);//なんぼでもメソッド追加できる
struct Virtual virtual;
};

// 継承元
struct Seed {
void (*func)();
};

// 似非継承
struct Inherit1 {
// 似非継承用スタブ
void (*inheritance)();
char *msg1;
};

// 似非継承
struct Inherit2 {
// 似非継承用スタブ
void (*inheritance)();
char *msg2;
};

// クラス見立てのただの構造体
struct Super class;
struct Interface interface;
struct Seed seed;
struct Inherit1 inherit1;
struct Inherit2 inherit2;

//struct Virtual *virtual = (struct Virtual *)&interface.virtual;
// 同じクラスでありながら、別々のインスタンスに同じメンバー
struct Virtual *virtual1 = (struct Virtual *)&interface.virtual;
struct Virtual *virtual2 = (struct Virtual *)&interface.virtual;

void overrider()
{
printf("Exceed Charge\n");
}

void overridden()
{
printf("Standing By\n");
}

void super_sub()
{
// インターフェース実装のプロトタイプ宣言は重要
void sub_method1();

virtual1->substance[0] = (void *)&super_method1;
virtual2->substance[1] = (void *)&super_method2;

interface.interface[0] = (void *)&sub_method1;

seed.func = (void *)&overrider;
inherit1.inheritance = (*seed.func);
}

void super()
{
super_sub();

virtual1->substance[0]();
virtual2->substance[1]();

interface.interface[0]();

inherit1.inheritance();

seed.func = (void *)&overridden;
inherit2.inheritance = (*seed.func);

inherit2.inheritance();
inherit2.inheritance = (*inherit1.inheritance);
inherit2.inheritance();
}

// サブ・クラスに提供する部分
struct Instance
{
struct Interface *newface;
volatile unsigned long *Object;
void (*method)();
unsigned char String[1];

int x;
int y;

int width;
int height;
};

struct Instance** parent;
struct Instance* package;

void *new(struct Instance* this)
{
this = (struct Instance *)malloc(sizeof(struct Instance));
if(this){
(*this).method = (void *)&super;
}

return(this);
}

void delete(struct Instance* this)
{
if(this) free(this);
}



sub.c
コード:
#include <memory.h>

#include "super.c" // import super.c

struct Inherit3 {
void (*method)();
char *msg3;
};

struct Inherit3* inherit3;

// インターフェースの実装はサブ・クラスで
void sub_method1()
{
printf("実体3\n");
}

void method(char *msg)
{
printf("%s\n", msg);
}

void Inherit3()
{
// このシーケンスは呼出し順序が重要!!!!
inherit3 = (struct Inherit3 *)&inherit2;
inherit3->method = (*inherit2.inheritance);

// 親のメソッドお試し
inherit3->method();
// 自メソッド化
inherit3->method = (void *)&method;

inherit3->msg3 = "Ready.";
}

int main()
{
struct Instance* sub_class;

sub_class = (struct Instance *)new(package);
if(sub_class) (*sub_class).method();
delete(sub_class);

Inherit3(); // コンストラクタもどき
(*inherit3).method(inherit3->msg3); // これこそが多態性

return(0);
}



ソースが読み難くなってきたので、もうこれが最期です。

[ メッセージ編集済み 編集者: コブラ 編集日時 2005-04-04 18:02 ]

スキルアップ/キャリアアップ(JOB@IT)